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 - 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
10
11#pragma once
12
13#include <cstddef>
14#include <cstdint>
15#include <type_traits>
16#include <limits>
17#include <iterator>
18
21#include "comms/details/tag.h"
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:28
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.