COMMS
Template library intended to help with implementation of communication protocols.
Loading...
Searching...
No Matches
access.h
Go to the documentation of this file.
1//
2// Copyright 2014 - 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
12
13#pragma once
14
16#include "comms/details/tag.h"
18
19#include <cstddef>
20#include <iterator>
21#include <limits>
22#include <type_traits>
23
24COMMS_GNU_WARNING_PUSH
25
26#if COMMS_IS_GCC_14 && defined(NDEBUG) && (COMMS_IS_CPP20 || COMMS_IS_CPP23)
27// Bug in gcc-14, reporting "free-nonheap-object" error when working with vectors and using C++20/23, but only in Release mode
28COMMS_GNU_WARNING_DISABLE("-Wfree-nonheap-object")
29#endif // #if COMMS_IS_GCC_14 && defined(NDEBUG) && (COMMS_IS_CPP20 || COMMS_IS_CPP23)
30
31namespace comms
32{
33
34namespace util
35{
36
37namespace traits
38{
39
40namespace endian
41{
42
44struct Big {};
45
47struct Little {};
48
49} // namespace endian
50
51} // namespace traits
52
53namespace details
54{
55
56template <typename T>
57using AccessSelectTypeItself = typename std::decay<T>::type;
58
59template <typename T>
60using AccessSelectIntType =
62 std::is_signed<T>::value
63 >::template Type<int, unsigned>;
64
65template <typename T>
66using AccessOptimisedValueType =
67 typename comms::util::LazyShallowConditional<
68 sizeof(T) >= sizeof(int)
69 >::template Type<
70 AccessSelectTypeItself,
71 AccessSelectIntType,
72 T
73 >;
74
75template <typename TUnsignedByteType, typename T>
76typename std::decay<T>::type signExtCommon(T value, std::size_t size)
77{
78 using ValueType = typename std::decay<T>::type;
79 static_assert(std::is_unsigned<ValueType>::value, "Type T must be unsigned");
80 static_assert(std::is_unsigned<TUnsignedByteType>::value,
81 "Type TUnsignedByteType must be unsigned");
82
83 static const std::size_t BinDigits =
84 std::numeric_limits<TUnsignedByteType>::digits;
85 static_assert(BinDigits % 8 == 0, "Byte size assumption is not valid");
86
87 ValueType mask =
88 static_cast<ValueType>(static_cast<ValueType>(1) << ((size * BinDigits) - 1));
89 if (value & mask) {
90 return static_cast<ValueType>(value | (~((mask << 1) - 1)));
91 }
92 return value;
93}
94
95template <typename...>
96class SignExt
97{
98 template <typename... TParams>
99 using FullSize = comms::details::tag::Tag1<>;
100
101 template <typename... TParams>
102 using PartialSize = comms::details::tag::Tag2<>;
103
104public:
105 template <std::size_t TSize, typename TByteType, typename T>
106 static typename std::decay<T>::type value(T val)
107 {
108 using ValueType = typename std::decay<T>::type;
109 using Tag =
110 typename comms::util::LazyShallowConditional<
111 sizeof(ValueType) == TSize
112 >::template Type<
113 FullSize,
114 PartialSize
115 >;
116
117 return valueInternal<TSize, TByteType>(val, Tag());
118 }
119
120private:
121
122 template <std::size_t TSize, typename TByteType, typename T, typename... TParams>
123 static typename std::decay<T>::type valueInternal(T val, FullSize<TParams...>)
124 {
125 return val;
126 }
127
128 template <std::size_t TSize, typename TByteType, typename T, typename... TParams>
129 static typename std::decay<T>::type valueInternal(T val, PartialSize<TParams...>)
130 {
131 using ValueType = typename std::decay<T>::type;
132 using UnsignedValueType = typename std::make_unsigned<ValueType>::type;
133 static_assert(std::is_integral<ValueType>::value, "T must be integer type");
134 using UnsignedByteType = typename std::make_unsigned<TByteType>::type;
135
136 auto castedValue = static_cast<UnsignedValueType>(val);
137 return static_cast<ValueType>(
138 signExtCommon<UnsignedByteType>(castedValue, TSize));
139 }
140};
141
142template <typename TIter>
143using AccessIteratorByteType = typename std::iterator_traits<typename std::decay<TIter>::type>::value_type;
144
145template <typename TIter>
146struct AccessContainerByteTypeDetector
147{
148 using Type = AccessIteratorByteType<TIter>;
149};
150
151template <typename TContainer>
152struct AccessContainerByteTypeDetector<std::back_insert_iterator<TContainer> >
153{
154 using Type = typename TContainer::value_type;
155};
156
157template <typename TContainer>
158struct AccessContainerByteTypeDetector<std::insert_iterator<TContainer> >
159{
160 using Type = typename TContainer::value_type;
161};
162
163template <typename TContainer>
164struct AccessContainerByteTypeDetector<std::front_insert_iterator<TContainer> >
165{
166 using Type = typename TContainer::value_type;
167};
168
169template <typename TIter>
170using AccessContainerByteType =
171 typename AccessContainerByteTypeDetector<typename std::decay<TIter>::type>::Type;
172
173template <typename TIter>
174using AccessByteType =
175 typename comms::util::LazyShallowConditional<
176 std::is_void<AccessIteratorByteType<TIter> >::value
177 >::template Type<
178 AccessContainerByteType,
179 AccessIteratorByteType,
180 TIter
181 >;
182
183template <typename T, typename TIter>
184void writeBigUnsigned(T value, std::size_t size, TIter& iter)
185{
186 using ValueType = typename std::decay<T>::type;
187 static_assert(std::is_unsigned<ValueType>::value, "T type must be unsigned");
188 static_assert(std::is_integral<ValueType>::value, "T must be integral type");
189
190 using ByteType = AccessByteType<TIter>;
191 static_assert(!std::is_void<ByteType>::value, "Invalid byte type");
192 using UnsignedByteType = typename std::make_unsigned<ByteType>::type;
193 static const std::size_t BinDigits =
194 std::numeric_limits<UnsignedByteType>::digits;
195 static_assert(BinDigits % 8 == 0, "Byte size assumption is not valid");
196
197 std::size_t remainingSize = size;
198 while (remainingSize > 0) {
199 std::size_t remaingShift = ((remainingSize - 1) * BinDigits);
200 auto byte = static_cast<ByteType>(value >> remaingShift);
201 *iter = byte;
202 ++iter;
203 --remainingSize;
204 }
205}
206
207template <typename T, typename TIter>
208void writeLittleUnsigned(T value, std::size_t size, TIter& iter)
209{
210 using ValueType = typename std::decay<T>::type;
211 static_assert(std::is_integral<ValueType>::value, "T must be integral type");
212 static_assert(std::is_unsigned<ValueType>::value, "T type must be unsigned");
213
214 using ByteType = AccessByteType<TIter>;
215 static_assert(!std::is_void<ByteType>::value, "Invalid byte type");
216 using UnsignedByteType = typename std::make_unsigned<ByteType>::type;
217 static const std::size_t BinDigits =
218 std::numeric_limits<UnsignedByteType>::digits;
219 static_assert(BinDigits % 8 == 0, "Byte size assumption is not valid");
220
221 std::size_t remainingSize = size;
222 while (remainingSize > 0) {
223 std::size_t remaingShift = ((size - remainingSize) * BinDigits);
224
225 auto byte = static_cast<ByteType>(value >> remaingShift);
226 *iter = byte;
227 ++iter;
228 --remainingSize;
229 }
230}
231
232template <typename TEndian>
233struct WriteUnsignedFuncWrapper;
234
235template <>
236struct WriteUnsignedFuncWrapper<traits::endian::Big>
237{
238 template <typename T, typename TIter>
239 static void write(T value, std::size_t size, TIter& iter)
240 {
241 writeBigUnsigned(value, size, iter);
242 }
243};
244
245template <>
246struct WriteUnsignedFuncWrapper<traits::endian::Little>
247{
248 template <typename T, typename TIter>
249 static void write(T value, std::size_t size, TIter& iter)
250 {
251 writeLittleUnsigned(value, size, iter);
252 }
253};
254
255template <typename TEndian, typename T, typename TIter>
256void write(T value, std::size_t size, TIter& iter)
257{
258 using ValueType = typename std::decay<T>::type;
259 static_assert(std::is_integral<ValueType>::value, "T must be integral type");
260 using UnsignedType = typename std::make_unsigned<ValueType>::type;
261 UnsignedType unsignedValue = static_cast<UnsignedType>(value);
262 WriteUnsignedFuncWrapper<TEndian>::write(unsignedValue, size, iter);
263}
264
265template <typename TEndian, typename T, typename TIter>
266void writeRandomAccess(T value, std::size_t size, TIter& iter)
267{
268 using ValueType = typename std::decay<T>::type;
269
270 static_assert(std::is_integral<ValueType>::value, "T must be integral type");
271 static_assert(
272 std::is_same<
273 typename std::iterator_traits<TIter>::iterator_category,
274 std::random_access_iterator_tag
275 >::value,
276 "TIter must be random access iterator");
277
278 using ByteType = AccessByteType<TIter>;
279 static_assert(!std::is_void<ByteType>::value, "Invalid byte type");
280 using UnsignedByteType = typename std::make_unsigned<ByteType>::type;
281 static_assert(!std::is_const<UnsignedByteType>::value, "Value must be updatable");
282
283 auto startPtr = reinterpret_cast<UnsignedByteType*>(&(*iter));
284 auto endPtr = startPtr;
285 write<TEndian>(value, size, endPtr);
286 iter += (endPtr - startPtr);
287}
288
289template <typename...>
290class WriteHelper
291{
292 template <typename... TParams>
293 using RandomAccessTag = comms::details::tag::Tag1<>;
294
295 template <typename... TParams>
296 using RegularTag = comms::details::tag::Tag2<>;
297
298 template <typename TIter>
299 using RandomAccessOrPointerTag =
300 typename comms::util::LazyShallowConditional<
301 std::is_pointer<TIter>::value &&
302 std::is_unsigned<AccessByteType<TIter> >::value
303 >::template Type<
304 RegularTag,
305 RandomAccessTag
306 >;
307
308 template <typename TIter>
309 using Tag =
310 typename comms::util::LazyShallowConditional<
311 std::is_same<
312 typename std::iterator_traits<TIter>::iterator_category,
313 std::random_access_iterator_tag
314 >::value
315 >::template Type<
316 RandomAccessOrPointerTag,
317 RegularTag,
318 TIter
319 >;
320
321 template <typename TEndian, typename T, typename TIter, typename... TParams>
322 static void writeInternal(T value, std::size_t size, TIter& iter, RandomAccessTag<TParams...>)
323 {
324 writeRandomAccess<TEndian>(value, size, iter);
325 }
326
327 template <typename TEndian, typename T, typename TIter, typename... TParams>
328 static void writeInternal(T value, std::size_t size, TIter& iter, RegularTag<TParams...>)
329 {
330 details::write<TEndian>(value, size, iter);
331 }
332
333public:
334 template <typename TEndian, typename T, typename TIter>
335 static void write(T value, std::size_t size, TIter& iter)
336 {
337 using ValueType = typename std::decay<T>::type;
338 using AccessOptimisedValueType = details::AccessOptimisedValueType<ValueType>;
339
340 return writeInternal<TEndian>(static_cast<AccessOptimisedValueType>(value), size, iter, Tag<TIter>());
341 }
342};
343
344template <typename T, typename TIter>
345T readBigUnsigned(std::size_t size, TIter& iter)
346{
347 using ValueType = typename std::decay<T>::type;
348 static_assert(std::is_integral<ValueType>::value, "T must be integral type");
349 static_assert(std::is_unsigned<ValueType>::value, "T type must be unsigned");
350
351 using ByteType = AccessByteType<TIter>;
352 static_assert(!std::is_void<ByteType>::value, "Invalid byte type");
353 using UnsignedByteType = typename std::make_unsigned<ByteType>::type;
354 static const std::size_t BinDigits =
355 std::numeric_limits<UnsignedByteType>::digits;
356 static_assert(BinDigits % 8 == 0, "Byte size assumption is not valid");
357
358 ValueType value = 0;
359 std::size_t remainingSize = size;
360 while (remainingSize > 0) {
361 auto byte = *iter;
362 value = static_cast<ValueType>(value << BinDigits);
363 value |= static_cast<decltype(value)>(static_cast<UnsignedByteType>(byte));
364 ++iter;
365 --remainingSize;
366 }
367 return value;
368}
369
370template <typename T, typename TIter>
371T readLittleUnsigned(std::size_t size, TIter& iter)
372{
373 using ValueType = typename std::decay<T>::type;
374 static_assert(std::is_integral<ValueType>::value, "T must be integral type");
375 static_assert(std::is_unsigned<ValueType>::value, "T type must be unsigned");
376
377 using ByteType = AccessByteType<TIter>;
378 static_assert(!std::is_void<ByteType>::value, "Invalid byte type");
379 using UnsignedByteType = typename std::make_unsigned<ByteType>::type;
380 static const std::size_t BinDigits =
381 std::numeric_limits<UnsignedByteType>::digits;
382 static_assert(BinDigits % 8 == 0, "Byte size assumption is not valid");
383
384 ValueType value = 0;
385 std::size_t remainingSize = size;
386 while (remainingSize > 0) {
387 auto byte = *iter;
388 value |= static_cast<ValueType>(static_cast<UnsignedByteType>(byte)) <<
389 ((size - remainingSize) * BinDigits);
390 ++iter;
391 --remainingSize;
392 }
393
394 return static_cast<T>(value);
395}
396
397template <typename TEndian>
398struct ReadUnsignedFuncWrapper;
399
400template <>
401struct ReadUnsignedFuncWrapper<traits::endian::Big>
402{
403 template <typename T, typename TIter>
404 static T read(std::size_t size, TIter& iter)
405 {
406 return readBigUnsigned<T>(size, iter);
407 }
408};
409
410template <>
411struct ReadUnsignedFuncWrapper<traits::endian::Little>
412{
413 template <typename T, typename TIter>
414 static T read(std::size_t size, TIter& iter)
415 {
416 return readLittleUnsigned<T>(size, iter);
417 }
418};
419
420template <typename TEndian, typename T, typename TIter>
421T read(std::size_t size, TIter& iter)
422{
423 using ValueType = typename std::decay<T>::type;
424
425 static_assert(std::is_integral<ValueType>::value, "T must be integral type");
426
427 using UnsignedType = typename std::make_unsigned<ValueType>::type;
428 auto value = ReadUnsignedFuncWrapper<TEndian>::template read<UnsignedType>(size, iter);
429 return static_cast<T>(static_cast<ValueType>(value));
430}
431
432template <typename TEndian, typename T, typename TIter>
433T readFromPointerToSigned(std::size_t size, TIter& iter)
434{
435 using ValueType = typename std::decay<T>::type;
436
437 static_assert(std::is_integral<ValueType>::value, "T must be integral type");
438 static_assert(
439 std::is_same<
440 typename std::iterator_traits<TIter>::iterator_category,
441 std::random_access_iterator_tag
442 >::value,
443 "TIter must be random access iterator");
444
445 using ByteType = AccessByteType<TIter>;
446 static_assert(!std::is_void<ByteType>::value, "Invalid byte type");
447 using UnsignedByteType = typename std::make_unsigned<ByteType>::type;
448
449 auto startPtr = reinterpret_cast<const UnsignedByteType*>(&(*iter));
450 auto endPtr = startPtr;
451 auto value = details::read<TEndian, ValueType>(size, endPtr);
452 iter += (endPtr - startPtr);
453 return static_cast<T>(static_cast<ValueType>(value));
454}
455
456template <typename...>
457class ReadHelper
458{
459 template <typename... TParams>
460 using PointerToSignedTag = comms::details::tag::Tag1<>;
461
462 template <typename... TParams>
463 using OtherTag = comms::details::tag::Tag2<>;
464
465 template <typename TIter>
466 using PointerCheckTag =
467 typename comms::util::LazyShallowConditional<
468 std::is_const<AccessByteType<TIter> >::value &&
469 std::is_unsigned<AccessByteType<TIter> >::value
470 >::template Type<
471 OtherTag,
472 PointerToSignedTag
473 >;
474
475 template <typename TIter>
476 using Tag =
477 typename comms::util::LazyShallowConditional<
478 std::is_pointer<TIter>::value
479 >::template Type<
480 PointerCheckTag,
481 OtherTag,
482 TIter
483 >;
484
485 template <typename TEndian, typename T, typename TIter, typename... TParams>
486 static T readInternal(std::size_t size, TIter& iter, PointerToSignedTag<TParams...>)
487 {
488 return readFromPointerToSigned<TEndian, T>(size, iter);
489 }
490
491 template <typename TEndian, typename T, typename TIter, typename... TParams>
492 static T readInternal(std::size_t size, TIter& iter, OtherTag<TParams...>)
493 {
494 return details::read<TEndian, T>(size, iter);
495 }
496
497public:
498 template <typename TEndian, typename T, typename TIter>
499 static T read(std::size_t size, TIter& iter)
500 {
501 using ValueType = typename std::decay<T>::type;
502 using AccessOptimisedValueType = details::AccessOptimisedValueType<ValueType>;
503 return
504 static_cast<ValueType>(
505 readInternal<TEndian, AccessOptimisedValueType>(size, iter, Tag<TIter>()));
506 }
507
508 template <typename TEndian, typename T, std::size_t TSize, typename TIter>
509 static T read(TIter& iter)
510 {
511 using ValueType = typename std::decay<T>::type;
512 using ByteType = details::AccessByteType<TIter>;
513 static_assert(!std::is_void<ByteType>::value, "Invalid byte type");
514 static_assert(TSize <= sizeof(ValueType), "Precondition failure");
515 auto retval = read<TEndian, ValueType>(TSize, iter);
516 if (std::is_signed<ValueType>::value) {
517 retval = details::SignExt<>::template value<TSize, ByteType>(retval);
518 }
519 return static_cast<T>(retval);
520 }
521};
522
523} // namespace details
524
534template <std::size_t TSize, typename T, typename TIter>
535void writeBig(T value, TIter& iter)
536{
537 details::WriteHelper<>::template write<traits::endian::Big>(value, TSize, iter);
538}
539
549template <typename T, typename TIter>
550void writeBig(T value, std::size_t size, TIter& iter)
551{
552 details::WriteHelper<>::template write<traits::endian::Big>(value, size, iter);
553}
554
562template <typename T, typename TIter>
563void writeBig(T value, TIter& iter)
564{
565 using ValueType = typename std::decay<T>::type;
566 writeBig<sizeof(ValueType)>(static_cast<ValueType>(value), iter);
567}
568
579template <typename T, std::size_t TSize, typename TIter>
580T readBig(TIter& iter)
581{
582 return details::ReadHelper<>::template read<traits::endian::Big, T, TSize>(iter);
583}
584
593template <typename T, typename TIter>
594T readBig(TIter& iter)
595{
596 using ValueType = typename std::decay<T>::type;
597 return static_cast<T>(readBig<ValueType, sizeof(ValueType)>(iter));
598}
599
609template <typename T, typename TIter>
610T readBig(TIter& iter, std::size_t size)
611{
612 return details::ReadHelper<>::template read<traits::endian::Big, T>(size, iter);
613}
614
624template <std::size_t TSize, typename T, typename TIter>
625void writeLittle(T value, TIter& iter)
626{
627 details::WriteHelper<>::template write<traits::endian::Little>(value, TSize, iter);
628}
629
639template <typename T, typename TIter>
640void writeLittle(T value, std::size_t size, TIter& iter)
641{
642 details::WriteHelper<>::template write<traits::endian::Little>(value, size, iter);
643}
644
652template <typename T, typename TIter>
653void writeLittle(T value, TIter& iter)
654{
655 using ValueType = typename std::decay<T>::type;
656 writeLittle<sizeof(ValueType)>(static_cast<ValueType>(value), iter);
657}
658
669template <typename T, std::size_t TSize, typename TIter>
670T readLittle(TIter& iter)
671{
672 return details::ReadHelper<>::template read<traits::endian::Little, T, TSize>(iter);
673}
674
683template <typename T, typename TIter>
684T readLittle(TIter& iter)
685{
686 using ValueType = typename std::decay<T>::type;
687 return static_cast<T>(readLittle<ValueType, sizeof(ValueType)>(iter));
688}
689
699template <typename T, typename TIter>
700T readLittle(TIter& iter, std::size_t size)
701{
702 return details::ReadHelper<>::template read<traits::endian::Little, T>(size, iter);
703}
704
706template <typename T, typename TIter>
708 T value,
709 TIter& iter,
710 const traits::endian::Big& endian)
711{
712 static_cast<void>(endian);
713 writeBig(value, iter);
714}
715
717template <std::size_t TSize, typename T, typename TIter>
719 T value,
720 TIter& iter,
721 const traits::endian::Big& endian)
722{
723 static_cast<void>(endian);
724 writeBig<TSize>(value, iter);
725}
726
728template <typename T, typename TIter>
730 T value,
731 std::size_t size,
732 TIter& iter,
733 const traits::endian::Big& endian)
734{
735 static_cast<void>(endian);
736 writeBig(value, size, iter);
737}
738
740template <typename T, typename TIter>
742 T value,
743 TIter& iter,
744 const traits::endian::Little& endian)
745{
746 static_cast<void>(endian);
747 writeLittle(value, iter);
748}
749
751template <std::size_t TSize, typename T, typename TIter>
753 T value,
754 TIter& iter,
755 const traits::endian::Little& endian)
756{
757 static_cast<void>(endian);
758 return writeLittle<TSize>(value, iter);
759}
760
762template <typename T, typename TIter>
764 T value,
765 std::size_t size,
766 TIter& iter,
767 const traits::endian::Little& endian)
768{
769 static_cast<void>(endian);
770 writeLittle(value, size, iter);
771}
772
774template <typename T, typename TIter>
775T readData(TIter& iter, const traits::endian::Big& endian)
776{
777 static_cast<void>(endian);
778 return readBig<T>(iter);
779}
780
782template <typename T, typename TIter>
783T readData(TIter& iter, std::size_t size, const traits::endian::Big& endian)
784{
785 static_cast<void>(endian);
786 return readBig<T>(iter, size);
787}
788
790template <typename T, std::size_t TSize, typename TIter>
791T readData(TIter& iter, const traits::endian::Big& endian)
792{
793 static_cast<void>(endian);
794 return readBig<T, TSize>(iter);
795}
796
798template <typename T, typename TIter>
799T readData(TIter& iter, const traits::endian::Little& endian)
800{
801 static_cast<void>(endian);
802 return readLittle<T>(iter);
803}
804
806template <typename T, std::size_t TSize, typename TIter>
807T readData(TIter& iter, const traits::endian::Little& endian)
808{
809 static_cast<void>(endian);
810 return readLittle<T, TSize>(iter);
811}
812
814template <typename T, typename TIter>
815T readData(TIter& iter, std::size_t size, const traits::endian::Little& endian)
816{
817 static_cast<void>(endian);
818 return readLittle<T>(iter, size);
819}
820
821} // namespace util
822
823} // namespace comms
824
825COMMS_GNU_WARNING_POP
Contains various compiler related definitions.
void writeLittle(T value, TIter &iter)
Write part of integral value into the output area using little endian notation.
Definition access.h:625
T readData(TIter &iter, const traits::endian::Big &endian)
Same as readBig<T, TIter>()
Definition access.h:775
void writeData(T value, TIter &iter, const traits::endian::Big &endian)
Same as writeBig<T, TIter>()
Definition access.h:707
T readBig(TIter &iter)
Read part of integral value from the input area using big endian notation.
Definition access.h:580
void writeBig(T value, TIter &iter)
Write part of integral value into the output area using big endian notation.
Definition access.h:535
T readLittle(TIter &iter)
Read part of integral value from the input area using little endian notation.
Definition access.h:670
Main namespace for all classes / functions of COMMS library.
STL namespace.
Replacement to std::conditional.
Definition type_traits.h:32
Empty class used in traits to indicate Big Endian.
Definition access.h:44
Empty class used in traits to indicate Little Endian.
Definition access.h:47
Replacement to some types from standard type_traits.