COMMS
Template library intended to help with implementation of communication protocols.
Loading...
Searching...
No Matches
AvailableLength.h
1//
2// Copyright 2021 - 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
8#pragma once
9
10#include "comms/Assert.h"
11#include "comms/cast.h"
12#include "comms/details/tag.h"
13#include "comms/ErrorStatus.h"
14#include "comms/util/SizeToType.h"
15#include "comms/util/access.h"
17
18#include <algorithm>
19#include <iterator>
20#include <limits>
21#include <type_traits>
22
23namespace comms
24{
25
26namespace field
27{
28
29namespace adapter
30{
31
32template <typename TBase>
33class AvailableLength : public TBase
34{
35 using BaseImpl = TBase;
36 using BaseSerialisedType = typename BaseImpl::SerialisedType;
37
38public:
39
40 using ValueType = typename BaseImpl::ValueType;
41 using SerialisedType = typename BaseImpl::SerialisedType;
42
43 using Endian = typename BaseImpl::Endian;
44
45 AvailableLength() = default;
46
47 explicit AvailableLength(const ValueType& val)
48 : BaseImpl(val)
49 {
50 }
51
52 explicit AvailableLength(ValueType&& val)
53 : BaseImpl(std::move(val))
54 {
55 }
56
57 AvailableLength(const AvailableLength&) = default;
58 AvailableLength(AvailableLength&&) = default;
59 AvailableLength& operator=(const AvailableLength&) = default;
60 AvailableLength& operator=(AvailableLength&&) = default;
61
62 void setForcedLength(int len)
63 {
64 m_forcedLength = len;
65 }
66
67 int getForcedLength() const
68 {
69 return m_forcedLength;
70 }
71
72 std::size_t length() const
73 {
74 if (m_forcedLength == 0) {
75 return BaseImpl::length();
76 }
77
78 if (0 < m_forcedLength) {
79 return std::min(BaseImpl::length(), static_cast<std::size_t>(m_forcedLength));
80 }
81
82 auto serValue = toSerialised(BaseImpl::getValue());
83 for (std::size_t len = 1U; len < sizeof(SerialisedType); ++len) {
84 if (fitsLength(serValue, len)) {
85 return len;
86 }
87 }
88
89 return BaseImpl::length();
90 }
91
92 static constexpr std::size_t minLength()
93 {
94 return 1U;
95 }
96
97 static constexpr std::size_t maxLength()
98 {
99 return BaseImpl::maxLength();
100 }
101
102 static constexpr SerialisedType toSerialised(ValueType val)
103 {
104 return static_cast<SerialisedType>(BaseImpl::toSerialised(val));
105 }
106
107 static constexpr ValueType fromSerialised(SerialisedType val)
108 {
109 return BaseImpl::fromSerialised(static_cast<BaseSerialisedType>(val));
110 }
111
112 template <typename TIter>
113 comms::ErrorStatus read(TIter& iter, std::size_t size)
114 {
115 if (size == 0U) {
117 }
118
119 auto fromIter = iter;
120 auto unsignedSerialized = util::readData<UnsignedSerialisedType>(iter, std::min(size, BaseImpl::maxLength()), Endian());
121 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
122 BaseImpl::setValue(fromSerialised(signExtUnsignedSerialised(unsignedSerialized, len, HasSignTag())));
124 }
125
126 static constexpr bool hasReadNoStatus()
127 {
128 return false;
129 }
130
131 template <typename TIter>
132 void readNoStatus(TIter& iter) = delete;
133
134 template <typename TIter>
135 comms::ErrorStatus write(TIter& iter, std::size_t size) const
136 {
137 if (m_forcedLength == 0) {
138 return BaseImpl::write(iter, size);
139 }
140
141 if (!BaseImpl::canWrite()) {
143 }
144
145 auto fieldLen = length();
146 if (size < fieldLen) {
148 }
149
150 auto serValue = toSerialised(BaseImpl::getValue());
151 if (0 < m_forcedLength) {
152 comms::util::writeData(serValue, fieldLen, iter, Endian());
154 }
155
156 // variable length, based on value
157 std::size_t len = 1U;
158 for (; len < sizeof(serValue); ++len) {
159 if (!fitsLength(serValue, len)) {
160 continue;
161 }
162
163 comms::util::writeData(serValue, len, iter, Endian());
165 }
166
167 return BaseImpl::write(iter, size);
168 }
169
170 static constexpr bool hasWriteNoStatus()
171 {
172 return false;
173 }
174
175 template <typename TIter>
176 void writeNoStatus(TIter& iter) const = delete;
177
178private:
179 template <typename... TParams>
180 using UnsignedTag = comms::details::tag::Tag1<>;
181
182 template <typename... TParams>
183 using SignedTag = comms::details::tag::Tag2<>;
184
185 using HasSignTag =
186 typename comms::util::LazyShallowConditional<
187 std::is_signed<SerialisedType>::value
188 >::template Type<
189 SignedTag,
190 UnsignedTag
191 >;
192
193 using UnsignedSerialisedType = typename std::make_unsigned<SerialisedType>::type;
194
195 template <typename... TParams>
196 static constexpr SerialisedType signExtUnsignedSerialised(
197 UnsignedSerialisedType val,
198 std::size_t,
199 UnsignedTag<TParams...>)
200 {
201 return static_cast<SerialisedType>(val);
202 }
203
204 template <typename... TParams>
205 static SerialisedType signExtUnsignedSerialised(
206 UnsignedSerialisedType val,
207 std::size_t bytesCount,
208 SignedTag<TParams...>)
209 {
210 UnsignedSerialisedType signBitMask =
211 static_cast<UnsignedSerialisedType>(1U) << ((bytesCount * BitsInByte) - (bytesCount + 1));
212
213 if ((val & signBitMask) == 0U) {
214 return static_cast<SerialisedType>(val);
215 }
216
217 UnsignedSerialisedType signExtMask =
218 static_cast<UnsignedSerialisedType>(~(signBitMask - 1));
219
220 val |= signExtMask;
221 return static_cast<SerialisedType>(val);
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 static bool fitsLength(SerialisedType val, std::size_t len)
260 {
261 if (sizeof(val) <= len) {
262 return true;
263 }
264
265 auto minValue = getMinLimitedValue(len, HasSignTag());
266 auto maxValue = getMaxLimitedValue(len, HasSignTag());
267 return ((minValue <= val) && (val <= maxValue));
268 }
269
270 static const std::size_t BitsInByte =
271 std::numeric_limits<std::uint8_t>::digits;
272
273 int m_forcedLength = -1;
274};
275
276} // namespace adapter
277
278} // namespace field
279
280} // namespace comms
281
This file contains classes required for generic custom assertion functionality.
#define COMMS_ASSERT(expr)
Generic assert macro.
Definition Assert.h:168
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:1471
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.