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