COMMS
Template library intended to help with implementation of communication protocols.
Loading...
Searching...
No Matches
AvailableLength.h
1//
2// Copyright 2021 - 2024 (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
8#pragma once
9
10#include <type_traits>
11#include <algorithm>
12#include <limits>
13
14#include "comms/Assert.h"
15#include "comms/util/SizeToType.h"
16#include "comms/util/access.h"
18#include "comms/ErrorStatus.h"
19#include "comms/details/tag.h"
20#include "comms/cast.h"
21
22namespace comms
23{
24
25namespace field
26{
27
28namespace adapter
29{
30
31template <typename TBase>
32class AvailableLength : public TBase
33{
34 using BaseImpl = TBase;
35 using BaseSerialisedType = typename BaseImpl::SerialisedType;
36
37public:
38
39 using ValueType = typename BaseImpl::ValueType;
40 using SerialisedType = typename BaseImpl::SerialisedType;
41
42 using Endian = typename BaseImpl::Endian;
43
44 AvailableLength() = default;
45
46 explicit AvailableLength(const ValueType& val)
47 : BaseImpl(val)
48 {
49 }
50
51 explicit AvailableLength(ValueType&& val)
52 : BaseImpl(std::move(val))
53 {
54 }
55
56 AvailableLength(const AvailableLength&) = default;
57 AvailableLength(AvailableLength&&) = default;
58 AvailableLength& operator=(const AvailableLength&) = default;
59 AvailableLength& operator=(AvailableLength&&) = default;
60
61 void setForcedLength(int len)
62 {
63 m_forcedLength = len;
64 }
65
66 int getForcedLength() const
67 {
68 return m_forcedLength;
69 }
70
71 std::size_t length() const
72 {
73 if (m_forcedLength == 0) {
74 return BaseImpl::length();
75 }
76
77 if (0 < m_forcedLength) {
78 return std::min(BaseImpl::length(), static_cast<std::size_t>(m_forcedLength));
79 }
80
81 auto serValue = toSerialised(BaseImpl::getValue());
82 for (std::size_t len = 1U; len < sizeof(SerialisedType); ++len) {
83 if (fitsLength(serValue, len)) {
84 return len;
85 }
86 }
87
88 return BaseImpl::length();
89 }
90
91 static constexpr std::size_t minLength()
92 {
93 return 1U;
94 }
95
96 static constexpr std::size_t maxLength()
97 {
98 return BaseImpl::maxLength();
99 }
100
101 static constexpr SerialisedType toSerialised(ValueType val)
102 {
103 return static_cast<SerialisedType>(BaseImpl::toSerialised(val));
104 }
105
106 static constexpr ValueType fromSerialised(SerialisedType val)
107 {
108 return BaseImpl::fromSerialised(static_cast<BaseSerialisedType>(val));
109 }
110
111 template <typename TIter>
112 comms::ErrorStatus read(TIter& iter, std::size_t size)
113 {
114 if (size == 0U) {
116 }
117
118 auto fromIter = iter;
119 auto unsignedSerialized = util::readData<UnsignedSerialisedType>(iter, std::min(size, BaseImpl::maxLength()), Endian());
120 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
121 BaseImpl::setValue(fromSerialised(signExtUnsignedSerialised(unsignedSerialized, len, HasSignTag())));
123 }
124
125 static constexpr bool hasReadNoStatus()
126 {
127 return false;
128 }
129
130 template <typename TIter>
131 void readNoStatus(TIter& iter) = delete;
132
133 template <typename TIter>
134 comms::ErrorStatus write(TIter& iter, std::size_t size) const
135 {
136 if (m_forcedLength == 0) {
137 return BaseImpl::write(iter, size);
138 }
139
140 if (!BaseImpl::canWrite()) {
142 }
143
144 auto fieldLen = length();
145 if (size < fieldLen) {
147 }
148
149 auto serValue = toSerialised(BaseImpl::getValue());
150 if (0 < m_forcedLength) {
151 comms::util::writeData(serValue, fieldLen, iter, Endian());
153 }
154
155 // variable length, based on value
156 std::size_t len = 1U;
157 for (; len < sizeof(serValue); ++len) {
158 if (!fitsLength(serValue, len)) {
159 continue;
160 }
161
162 comms::util::writeData(serValue, len, iter, Endian());
164 }
165
166 return BaseImpl::write(iter, size);
167 }
168
169 static constexpr bool hasWriteNoStatus()
170 {
171 return false;
172 }
173
174 template <typename TIter>
175 void writeNoStatus(TIter& iter) const = delete;
176
177private:
178 template <typename... TParams>
179 using UnsignedTag = comms::details::tag::Tag1<>;
180
181 template <typename... TParams>
182 using SignedTag = comms::details::tag::Tag2<>;
183
184 using HasSignTag =
185 typename comms::util::LazyShallowConditional<
186 std::is_signed<SerialisedType>::value
187 >::template Type<
188 SignedTag,
189 UnsignedTag
190 >;
191
192 using UnsignedSerialisedType = typename std::make_unsigned<SerialisedType>::type;
193
194 template <typename... TParams>
195 static constexpr SerialisedType signExtUnsignedSerialised(
196 UnsignedSerialisedType val,
197 std::size_t,
198 UnsignedTag<TParams...>)
199 {
200 return static_cast<SerialisedType>(val);
201 }
202
203 template <typename... TParams>
204 static SerialisedType signExtUnsignedSerialised(
205 UnsignedSerialisedType val,
206 std::size_t bytesCount,
207 SignedTag<TParams...>)
208 {
209 UnsignedSerialisedType signBitMask =
210 static_cast<UnsignedSerialisedType>(1U) << ((bytesCount * BitsInByte) - (bytesCount + 1));
211
212 if ((val & signBitMask) == 0U) {
213 return static_cast<SerialisedType>(val);
214 }
215
216 UnsignedSerialisedType signExtMask =
217 static_cast<UnsignedSerialisedType>(~(signBitMask - 1));
218
219 val |= signExtMask;
220 return static_cast<SerialisedType>(val);
221 }
222
223
224 template <typename... TParams>
225 static SerialisedType getMinLimitedValue(std::size_t len, UnsignedTag<TParams...>)
226 {
227 static_cast<void>(len);
228 return static_cast<SerialisedType>(0);
229 }
230
231 template <typename... TParams>
232 static SerialisedType getMinLimitedValue(std::size_t len, SignedTag<TParams...>)
233 {
234 COMMS_ASSERT(len < sizeof(UnsignedSerialisedType));
235 auto mask =
236 ((static_cast<UnsignedSerialisedType>(1U) << ((len * BitsInByte) - 1)) - 1U);
237
238 return static_cast<SerialisedType>(~static_cast<UnsignedSerialisedType>(mask));
239 }
240
241 template <typename... TParams>
242 static SerialisedType getMaxLimitedValue(std::size_t len, UnsignedTag<TParams...>)
243 {
244 COMMS_ASSERT(len < sizeof(UnsignedSerialisedType));
245 auto value =
246 ((static_cast<UnsignedSerialisedType>(1U) << (len * BitsInByte)) - 1U);
247 return static_cast<SerialisedType>(value);
248 }
249
250 template <typename... TParams>
251 static SerialisedType getMaxLimitedValue(std::size_t len, SignedTag<TParams...>)
252 {
253 COMMS_ASSERT(len < sizeof(UnsignedSerialisedType));
254 auto value =
255 ((static_cast<UnsignedSerialisedType>(1U) << ((len * BitsInByte) - 1)) - 1U);
256 return static_cast<SerialisedType>(value);
257 }
258
259
260 static bool fitsLength(SerialisedType val, std::size_t len)
261 {
262 if (sizeof(val) <= len) {
263 return true;
264 }
265
266 auto minValue = getMinLimitedValue(len, HasSignTag());
267 auto maxValue = getMaxLimitedValue(len, HasSignTag());
268 return ((minValue <= val) && (val <= maxValue));
269 }
270
271 static const std::size_t BitsInByte =
272 std::numeric_limits<std::uint8_t>::digits;
273
274
275 int m_forcedLength = -1;
276};
277
278} // namespace adapter
279
280} // namespace field
281
282} // namespace comms
283
284
This file contains classes required for generic custom assertion functionality.
#define COMMS_ASSERT(expr)
Generic assert macro.
Definition Assert.h:170
This file contain definition of error statuses used by comms module.
Contains functions for raw data access / (de)serialization.
Contains definition of various casts.
comms::option::def::Endian< TEndian > Endian
Same as comms::option::def::Endian.
Definition options.h:1438
void writeData(T value, TIter &iter, const traits::endian::Big &endian)
Same as writeBig<T, TIter>()
Definition access.h:706
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.
@ InvalidMsgData
Used to indicate that a message has invalid data.
STL namespace.
Replacement to some types from standard type_traits.