COMMS
Template library intended to help with implementation of communication protocols.
Loading...
Searching...
No Matches
FixedBitLength.h
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
10#pragma once
11
12#include "comms/Assert.h"
13#include "comms/details/tag.h"
14#include "comms/ErrorStatus.h"
15#include "comms/util/SizeToType.h"
16#include "comms/util/BitSizeToByteSize.h"
18
19#include <cstddef>
20#include <limits>
21
22namespace comms
23{
24
25namespace field
26{
27
28namespace adapter
29{
30
31template <std::size_t TLen, bool TSignExtend, typename TBase>
32class FixedBitLength : public TBase
33{
34 using BaseImpl = TBase;
35 using BaseSerialisedType = typename BaseImpl::SerialisedType;
36 using BaseUnsignedSerialisedType = typename std::make_unsigned<BaseSerialisedType>::type;
37
38 static const std::size_t BitLength = TLen;
39 static const std::size_t Length =
40 comms::util::BitSizeToByteSize<BitLength>::Value;
41
42 static_assert(0 < BitLength, "Bit length is expected to be greater than 0");
43 static_assert(Length <= sizeof(BaseSerialisedType),
44 "The provided length limit is too big");
45
46public:
47
48 using ValueType = typename BaseImpl::ValueType;
49
50 using SerialisedType =
52 (Length < sizeof(BaseSerialisedType))
53 >::template Type<
54 typename comms::util::SizeToType<Length, std::is_signed<BaseSerialisedType>::value>::Type,
55 BaseSerialisedType
56 >;
57
58 using Endian = typename BaseImpl::Endian;
59
60 FixedBitLength() = default;
61
62 explicit FixedBitLength(const ValueType& val)
63 : BaseImpl(val)
64 {
65 }
66
67 FixedBitLength(const FixedBitLength&) = default;
68 FixedBitLength(FixedBitLength&&) = default;
69 FixedBitLength& operator=(const FixedBitLength&) = default;
70 FixedBitLength& operator=(FixedBitLength&&) = default;
71
72 static constexpr std::size_t length()
73 {
74 return Length;
75 }
76
77 static constexpr std::size_t minLength()
78 {
79 return length();
80 }
81
82 static constexpr std::size_t maxLength()
83 {
84 return length();
85 }
86
87 static constexpr SerialisedType toSerialised(ValueType val)
88 {
89 return adjustToSerialised(BaseImpl::toSerialised(val), HasSignTag<>());
90 }
91
92 static constexpr ValueType fromSerialised(SerialisedType val)
93 {
94 return BaseImpl::fromSerialised(adjustFromSerialised(val, HasSignTag<>()));
95 }
96
97 template <typename TIter>
98 comms::ErrorStatus read(TIter& iter, std::size_t size)
99 {
100 if (size < length()) {
102 }
103
104 readNoStatus(iter);
106 }
107
108 template <typename TIter>
109 void readNoStatus(TIter& iter)
110 {
111 auto serialisedValue =
112 comms::util::readData<SerialisedType, Length>(iter, Endian());
113 BaseImpl::setValue(fromSerialised(serialisedValue));
114 }
115
116 template <typename TIter>
117 comms::ErrorStatus write(TIter& iter, std::size_t size) const
118 {
119 if (size < length()) {
121 }
122
123 writeNoStatus(iter);
125 }
126
127 template <typename TIter>
128 void writeNoStatus(TIter& iter) const
129 {
130 BaseImpl::template writeData<Length>(toSerialised(BaseImpl::getValue()), iter);
131 }
132
133private:
134 template <typename... TParams>
135 using UnsignedTag = comms::details::tag::Tag1<>;
136
137 template <typename... TParams>
138 using SignedTag = comms::details::tag::Tag2<>;
139
140 template <typename... TParams>
141 using NoSignExtTag = comms::details::tag::Tag3<>;
142
143 template <typename... TParams>
144 using MustSignExtTag = comms::details::tag::Tag4<>;
145
146 template <typename...>
147 using HasSignTag =
148 typename comms::util::LazyShallowConditional<
149 TSignExtend && std::is_signed<SerialisedType>::value
150 >::template Type<
151 SignedTag,
152 UnsignedTag
153 >;
154
155 using UnsignedSerialisedType = typename std::make_unsigned<SerialisedType>::type;
156
157 template <typename... TParams>
158 static SerialisedType adjustToSerialised(BaseSerialisedType val, UnsignedTag<TParams...>)
159 {
160 return static_cast<SerialisedType>(static_cast<BaseUnsignedSerialisedType>(val) & UnsignedValueMask);
161 }
162
163 template <typename... TParams>
164 static SerialisedType adjustToSerialised(BaseSerialisedType val, SignedTag<TParams...>)
165 {
166 auto valueTmp =
167 static_cast<UnsignedSerialisedType>(
168 static_cast<BaseUnsignedSerialisedType>(val) & UnsignedValueMask);
169
170 return signExtUnsignedSerialised(valueTmp);
171 }
172
173 template <typename... TParams>
174 static BaseSerialisedType adjustFromSerialised(SerialisedType val, UnsignedTag<TParams...>)
175 {
176 return static_cast<BaseSerialisedType>(static_cast<UnsignedSerialisedType>(val) & UnsignedValueMask);
177 }
178
179 template <typename... TParams>
180 static BaseSerialisedType adjustFromSerialised(SerialisedType val, SignedTag<TParams...>)
181 {
182 auto valueTmp =
183 static_cast<UnsignedSerialisedType>(
184 static_cast<UnsignedSerialisedType>(val) & UnsignedValueMask);
185 return
186 static_cast<BaseSerialisedType>(
187 signExtUnsignedSerialised(valueTmp));
188 }
189
190 static SerialisedType signExtUnsignedSerialised(UnsignedSerialisedType val)
191 {
192 using SignExtTag =
193 typename comms::util::LazyShallowConditional<
194 BitLength < static_cast<std::size_t>(std::numeric_limits<UnsignedSerialisedType>::digits)
195 >::template Type<
196 MustSignExtTag,
197 NoSignExtTag
198 >;
199 return signExtUnsignedSerialisedInternal(val, SignExtTag());
200 }
201
202 template <typename... TParams>
203 static SerialisedType signExtUnsignedSerialisedInternal(UnsignedSerialisedType val, MustSignExtTag<TParams...>)
204 {
205 static_assert(
206 BitLength < std::numeric_limits<UnsignedSerialisedType>::digits,
207 "BitLength is expected to be less than number of bits in the value type");
208
209 static const auto SignExtMask =
210 static_cast<UnsignedSerialisedType>(
211 ~((static_cast<UnsignedSerialisedType>(1U) << BitLength) - 1));
212
213 static const auto SignMask =
214 static_cast<UnsignedSerialisedType>(
215 static_cast<UnsignedSerialisedType>(1U) << (BitLength - 1));
216
217 if ((val & SignMask) != 0) {
218 val |= SignExtMask;
219 }
220 return static_cast<SerialisedType>(val);
221 }
222
223 template <typename... TParams>
224 static SerialisedType signExtUnsignedSerialisedInternal(UnsignedSerialisedType val, NoSignExtTag<TParams...>)
225 {
226 return static_cast<SerialisedType>(val);
227 }
228
229private:
230 static const UnsignedSerialisedType UnsignedValueMask =
231 (static_cast<UnsignedSerialisedType>(1U) << BitLength) - 1;
232};
233
234} // namespace adapter
235
236} // namespace field
237
238} // namespace comms
239
This file contains classes required for generic custom assertion functionality.
This file contain definition of error statuses used by comms module.
comms::option::def::Endian< TEndian > Endian
Same as comms::option::def::Endian.
Definition options.h:1473
comms::option::def::FixedBitLength< TLen, TSignExtend > FixedBitLength
Same as comms::option::def::FixedBitLength.
Definition options.h:1542
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.
Replacement to std::conditional.
Definition type_traits.h:32
Replacement to some types from standard type_traits.