COMMS
Template library intended to help with implementation of communication protocols.
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 
19 #include "comms/util/type_traits.h"
20 #include "comms/details/tag.h"
21 
22 namespace comms
23 {
24 
25 namespace util
26 {
27 
28 namespace traits
29 {
30 
31 namespace endian
32 {
33 
35 struct Big {};
36 
38 struct Little {};
39 
40 } // namespace endian
41 
42 } // namespace traits
43 
44 namespace details
45 {
46 
47 template <typename T>
48 using AccessSelectTypeItself = typename std::decay<T>::type;
49 
50 template <typename T>
51 using AccessSelectIntType =
52  typename comms::util::Conditional<
53  std::is_signed<T>::value
54  >::template Type<int, unsigned>;
55 
56 template <typename T>
57 using AccessOptimisedValueType =
58  typename comms::util::LazyShallowConditional<
59  sizeof(T) >= sizeof(int)
60  >::template Type<
61  AccessSelectTypeItself,
62  AccessSelectIntType,
63  T
64  >;
65 
66 template <typename TUnsignedByteType, typename T>
67 typename std::decay<T>::type signExtCommon(T value, std::size_t size)
68 {
69  using ValueType = typename std::decay<T>::type;
70  static_assert(std::is_unsigned<ValueType>::value, "Type T must be unsigned");
71  static_assert(std::is_unsigned<TUnsignedByteType>::value,
72  "Type TUnsignedByteType must be unsigned");
73 
74  static const std::size_t BinDigits =
75  std::numeric_limits<TUnsignedByteType>::digits;
76  static_assert(BinDigits % 8 == 0, "Byte size assumption is not valid");
77 
78  ValueType mask =
79  static_cast<ValueType>(static_cast<ValueType>(1) << ((size * BinDigits) - 1));
80  if (value & mask) {
81  return static_cast<ValueType>(value | (~((mask << 1) - 1)));
82  }
83  return value;
84 }
85 
86 template <typename...>
87 class SignExt
88 {
89  template <typename... TParams>
90  using FullSize = comms::details::tag::Tag1<>;
91 
92  template <typename... TParams>
93  using PartialSize = comms::details::tag::Tag2<>;
94 
95 public:
96  template <std::size_t TSize, typename TByteType, typename T>
97  static typename std::decay<T>::type value(T val)
98  {
99  using ValueType = typename std::decay<T>::type;
100  using Tag =
101  typename comms::util::LazyShallowConditional<
102  sizeof(ValueType) == TSize
103  >::template Type<
104  FullSize,
105  PartialSize
106  >;
107 
108  return valueInternal<TSize, TByteType>(val, Tag());
109  }
110 
111 private:
112 
113  template <std::size_t TSize, typename TByteType, typename T, typename... TParams>
114  static typename std::decay<T>::type valueInternal(T val, FullSize<TParams...>)
115  {
116  return val;
117  }
118 
119  template <std::size_t TSize, typename TByteType, typename T, typename... TParams>
120  static typename std::decay<T>::type valueInternal(T val, PartialSize<TParams...>)
121  {
122  using ValueType = typename std::decay<T>::type;
123  using UnsignedValueType = typename std::make_unsigned<ValueType>::type;
124  static_assert(std::is_integral<ValueType>::value, "T must be integer type");
125  using UnsignedByteType = typename std::make_unsigned<TByteType>::type;
126 
127  auto castedValue = static_cast<UnsignedValueType>(val);
128  return static_cast<ValueType>(
129  signExtCommon<UnsignedByteType>(castedValue, TSize));
130  }
131 };
132 
133 template <typename TIter>
134 using AccessIteratorByteType = typename std::iterator_traits<typename std::decay<TIter>::type>::value_type;
135 
136 template <typename TIter>
137 struct AccessContainerByteTypeDetector
138 {
139  using Type = AccessIteratorByteType<TIter>;
140 };
141 
142 template <typename TContainer>
143 struct AccessContainerByteTypeDetector<std::back_insert_iterator<TContainer> >
144 {
145  using Type = typename TContainer::value_type;
146 };
147 
148 template <typename TContainer>
149 struct AccessContainerByteTypeDetector<std::insert_iterator<TContainer> >
150 {
151  using Type = typename TContainer::value_type;
152 };
153 
154 template <typename TContainer>
155 struct AccessContainerByteTypeDetector<std::front_insert_iterator<TContainer> >
156 {
157  using Type = typename TContainer::value_type;
158 };
159 
160 template <typename TIter>
161 using AccessContainerByteType =
162  typename AccessContainerByteTypeDetector<typename std::decay<TIter>::type>::Type;
163 
164 template <typename TIter>
165 using AccessByteType =
166  typename comms::util::LazyShallowConditional<
167  std::is_void<AccessIteratorByteType<TIter> >::value
168  >::template Type<
169  AccessContainerByteType,
170  AccessIteratorByteType,
171  TIter
172  >;
173 
174 template <typename T, typename TIter>
175 void writeBigUnsigned(T value, std::size_t size, TIter& iter)
176 {
177  using ValueType = typename std::decay<T>::type;
178  static_assert(std::is_unsigned<ValueType>::value, "T type must be unsigned");
179  static_assert(std::is_integral<ValueType>::value, "T must be integral type");
180 
181  using ByteType = AccessByteType<TIter>;
182  static_assert(!std::is_void<ByteType>::value, "Invalid byte type");
183  using UnsignedByteType = typename std::make_unsigned<ByteType>::type;
184  static const std::size_t BinDigits =
185  std::numeric_limits<UnsignedByteType>::digits;
186  static_assert(BinDigits % 8 == 0, "Byte size assumption is not valid");
187 
188  std::size_t remainingSize = size;
189  while (remainingSize > 0) {
190  std::size_t remaingShift = ((remainingSize - 1) * BinDigits);
191  auto byte = static_cast<ByteType>(value >> remaingShift);
192  *iter = byte;
193  ++iter;
194  --remainingSize;
195  }
196 }
197 
198 template <typename T, typename TIter>
199 void writeLittleUnsigned(T value, std::size_t size, TIter& iter)
200 {
201  using ValueType = typename std::decay<T>::type;
202  static_assert(std::is_integral<ValueType>::value, "T must be integral type");
203  static_assert(std::is_unsigned<ValueType>::value, "T type must be unsigned");
204 
205  using ByteType = AccessByteType<TIter>;
206  static_assert(!std::is_void<ByteType>::value, "Invalid byte type");
207  using UnsignedByteType = typename std::make_unsigned<ByteType>::type;
208  static const std::size_t BinDigits =
209  std::numeric_limits<UnsignedByteType>::digits;
210  static_assert(BinDigits % 8 == 0, "Byte size assumption is not valid");
211 
212  std::size_t remainingSize = size;
213  while (remainingSize > 0) {
214  std::size_t remaingShift = ((size - remainingSize) * BinDigits);
215 
216  auto byte = static_cast<ByteType>(value >> remaingShift);
217  *iter = byte;
218  ++iter;
219  --remainingSize;
220  }
221 }
222 
223 template <typename TEndian>
224 struct WriteUnsignedFuncWrapper;
225 
226 template <>
227 struct WriteUnsignedFuncWrapper<traits::endian::Big>
228 {
229  template <typename T, typename TIter>
230  static void write(T value, std::size_t size, TIter& iter)
231  {
232  writeBigUnsigned(value, size, iter);
233  }
234 };
235 
236 template <>
237 struct WriteUnsignedFuncWrapper<traits::endian::Little>
238 {
239  template <typename T, typename TIter>
240  static void write(T value, std::size_t size, TIter& iter)
241  {
242  writeLittleUnsigned(value, size, iter);
243  }
244 };
245 
246 template <typename TEndian, typename T, typename TIter>
247 void write(T value, std::size_t size, TIter& iter)
248 {
249  using ValueType = typename std::decay<T>::type;
250  static_assert(std::is_integral<ValueType>::value, "T must be integral type");
251  using UnsignedType = typename std::make_unsigned<ValueType>::type;
252  UnsignedType unsignedValue = static_cast<UnsignedType>(value);
253  WriteUnsignedFuncWrapper<TEndian>::write(unsignedValue, size, iter);
254 }
255 
256 template <typename TEndian, typename T, typename TIter>
257 void writeRandomAccess(T value, std::size_t size, TIter& iter)
258 {
259  using ValueType = typename std::decay<T>::type;
260 
261  static_assert(std::is_integral<ValueType>::value, "T must be integral type");
262  static_assert(
263  std::is_same<
264  typename std::iterator_traits<TIter>::iterator_category,
265  std::random_access_iterator_tag
266  >::value,
267  "TIter must be random access iterator");
268 
269  using ByteType = AccessByteType<TIter>;
270  static_assert(!std::is_void<ByteType>::value, "Invalid byte type");
271  using UnsignedByteType = typename std::make_unsigned<ByteType>::type;
272  static_assert(!std::is_const<UnsignedByteType>::value, "Value must be updatable");
273 
274  auto startPtr = reinterpret_cast<UnsignedByteType*>(&(*iter));
275  auto endPtr = startPtr;
276  write<TEndian>(value, size, endPtr);
277  iter += (endPtr - startPtr);
278 }
279 
280 template <typename...>
281 class WriteHelper
282 {
283  template <typename... TParams>
284  using RandomAccessTag = comms::details::tag::Tag1<>;
285 
286  template <typename... TParams>
287  using RegularTag = comms::details::tag::Tag2<>;
288 
289  template <typename TIter>
290  using RandomAccessOrPointerTag =
291  typename comms::util::LazyShallowConditional<
292  std::is_pointer<TIter>::value &&
293  std::is_unsigned<AccessByteType<TIter> >::value
294  >::template Type<
295  RegularTag,
296  RandomAccessTag
297  >;
298 
299  template <typename TIter>
300  using Tag =
301  typename comms::util::LazyShallowConditional<
302  std::is_same<
303  typename std::iterator_traits<TIter>::iterator_category,
304  std::random_access_iterator_tag
305  >::value
306  >::template Type<
307  RandomAccessOrPointerTag,
308  RegularTag,
309  TIter
310  >;
311 
312  template <typename TEndian, typename T, typename TIter, typename... TParams>
313  static void writeInternal(T value, std::size_t size, TIter& iter, RandomAccessTag<TParams...>)
314  {
315  writeRandomAccess<TEndian>(value, size, iter);
316  }
317 
318  template <typename TEndian, typename T, typename TIter, typename... TParams>
319  static void writeInternal(T value, std::size_t size, TIter& iter, RegularTag<TParams...>)
320  {
321  details::write<TEndian>(value, size, iter);
322  }
323 
324 public:
325  template <typename TEndian, typename T, typename TIter>
326  static void write(T value, std::size_t size, TIter& iter)
327  {
328  using ValueType = typename std::decay<T>::type;
329  using AccessOptimisedValueType = details::AccessOptimisedValueType<ValueType>;
330 
331  return writeInternal<TEndian>(static_cast<AccessOptimisedValueType>(value), size, iter, Tag<TIter>());
332  }
333 };
334 
335 template <typename T, typename TIter>
336 T readBigUnsigned(std::size_t size, TIter& iter)
337 {
338  using ValueType = typename std::decay<T>::type;
339  static_assert(std::is_integral<ValueType>::value, "T must be integral type");
340  static_assert(std::is_unsigned<ValueType>::value, "T type must be unsigned");
341 
342  using ByteType = AccessByteType<TIter>;
343  static_assert(!std::is_void<ByteType>::value, "Invalid byte type");
344  using UnsignedByteType = typename std::make_unsigned<ByteType>::type;
345  static const std::size_t BinDigits =
346  std::numeric_limits<UnsignedByteType>::digits;
347  static_assert(BinDigits % 8 == 0, "Byte size assumption is not valid");
348 
349  ValueType value = 0;
350  std::size_t remainingSize = size;
351  while (remainingSize > 0) {
352  auto byte = *iter;
353  value = static_cast<ValueType>(value << BinDigits);
354  value |= static_cast<decltype(value)>(static_cast<UnsignedByteType>(byte));
355  ++iter;
356  --remainingSize;
357  }
358  return value;
359 }
360 
361 template <typename T, typename TIter>
362 T readLittleUnsigned(std::size_t size, TIter& iter)
363 {
364  using ValueType = typename std::decay<T>::type;
365  static_assert(std::is_integral<ValueType>::value, "T must be integral type");
366  static_assert(std::is_unsigned<ValueType>::value, "T type must be unsigned");
367 
368  using ByteType = AccessByteType<TIter>;
369  static_assert(!std::is_void<ByteType>::value, "Invalid byte type");
370  using UnsignedByteType = typename std::make_unsigned<ByteType>::type;
371  static const std::size_t BinDigits =
372  std::numeric_limits<UnsignedByteType>::digits;
373  static_assert(BinDigits % 8 == 0, "Byte size assumption is not valid");
374 
375  ValueType value = 0;
376  std::size_t remainingSize = size;
377  while (remainingSize > 0) {
378  auto byte = *iter;
379  value |= static_cast<ValueType>(static_cast<UnsignedByteType>(byte)) <<
380  ((size - remainingSize) * BinDigits);
381  ++iter;
382  --remainingSize;
383  }
384 
385  return static_cast<T>(value);
386 }
387 
388 template <typename TEndian>
389 struct ReadUnsignedFuncWrapper;
390 
391 template <>
392 struct ReadUnsignedFuncWrapper<traits::endian::Big>
393 {
394  template <typename T, typename TIter>
395  static T read(std::size_t size, TIter& iter)
396  {
397  return readBigUnsigned<T>(size, iter);
398  }
399 };
400 
401 template <>
402 struct ReadUnsignedFuncWrapper<traits::endian::Little>
403 {
404  template <typename T, typename TIter>
405  static T read(std::size_t size, TIter& iter)
406  {
407  return readLittleUnsigned<T>(size, iter);
408  }
409 };
410 
411 template <typename TEndian, typename T, typename TIter>
412 T read(std::size_t size, TIter& iter)
413 {
414  using ValueType = typename std::decay<T>::type;
415 
416  static_assert(std::is_integral<ValueType>::value, "T must be integral type");
417 
418  using UnsignedType = typename std::make_unsigned<ValueType>::type;
419  auto value = ReadUnsignedFuncWrapper<TEndian>::template read<UnsignedType>(size, iter);
420  return static_cast<T>(static_cast<ValueType>(value));
421 }
422 
423 template <typename TEndian, typename T, typename TIter>
424 T readFromPointerToSigned(std::size_t size, TIter& iter)
425 {
426  using ValueType = typename std::decay<T>::type;
427 
428  static_assert(std::is_integral<ValueType>::value, "T must be integral type");
429  static_assert(
430  std::is_same<
431  typename std::iterator_traits<TIter>::iterator_category,
432  std::random_access_iterator_tag
433  >::value,
434  "TIter must be random access iterator");
435 
436  using ByteType = AccessByteType<TIter>;
437  static_assert(!std::is_void<ByteType>::value, "Invalid byte type");
438  using UnsignedByteType = typename std::make_unsigned<ByteType>::type;
439 
440  auto startPtr = reinterpret_cast<const UnsignedByteType*>(&(*iter));
441  auto endPtr = startPtr;
442  auto value = details::read<TEndian, ValueType>(size, endPtr);
443  iter += (endPtr - startPtr);
444  return static_cast<T>(static_cast<ValueType>(value));
445 }
446 
447 template <typename...>
448 class ReadHelper
449 {
450  template <typename... TParams>
451  using PointerToSignedTag = comms::details::tag::Tag1<>;
452 
453  template <typename... TParams>
454  using OtherTag = comms::details::tag::Tag2<>;
455 
456  template <typename TIter>
457  using PointerCheckTag =
458  typename comms::util::LazyShallowConditional<
459  std::is_const<AccessByteType<TIter> >::value &&
460  std::is_unsigned<AccessByteType<TIter> >::value
461  >::template Type<
462  OtherTag,
463  PointerToSignedTag
464  >;
465 
466  template <typename TIter>
467  using Tag =
468  typename comms::util::LazyShallowConditional<
469  std::is_pointer<TIter>::value
470  >::template Type<
471  PointerCheckTag,
472  OtherTag,
473  TIter
474  >;
475 
476  template <typename TEndian, typename T, typename TIter, typename... TParams>
477  static T readInternal(std::size_t size, TIter& iter, PointerToSignedTag<TParams...>)
478  {
479  return readFromPointerToSigned<TEndian, T>(size, iter);
480  }
481 
482  template <typename TEndian, typename T, typename TIter, typename... TParams>
483  static T readInternal(std::size_t size, TIter& iter, OtherTag<TParams...>)
484  {
485  return details::read<TEndian, T>(size, iter);
486  }
487 
488 public:
489  template <typename TEndian, typename T, typename TIter>
490  static T read(std::size_t size, TIter& iter)
491  {
492  using ValueType = typename std::decay<T>::type;
493  using AccessOptimisedValueType = details::AccessOptimisedValueType<ValueType>;
494  return
495  static_cast<ValueType>(
496  readInternal<TEndian, AccessOptimisedValueType>(size, iter, Tag<TIter>()));
497  }
498 
499  template <typename TEndian, typename T, std::size_t TSize, typename TIter>
500  static T read(TIter& iter)
501  {
502  using ValueType = typename std::decay<T>::type;
503  using ByteType = details::AccessByteType<TIter>;
504  static_assert(!std::is_void<ByteType>::value, "Invalid byte type");
505  static_assert(TSize <= sizeof(ValueType), "Precondition failure");
506  auto retval = read<TEndian, ValueType>(TSize, iter);
507  if (std::is_signed<ValueType>::value) {
508  retval = details::SignExt<>::template value<TSize, ByteType>(retval);
509  }
510  return static_cast<T>(retval);
511  }
512 };
513 
514 } // namespace details
515 
525 template <std::size_t TSize, typename T, typename TIter>
526 void writeBig(T value, TIter& iter)
527 {
528  details::WriteHelper<>::template write<traits::endian::Big>(value, TSize, iter);
529 }
530 
540 template <typename T, typename TIter>
541 void writeBig(T value, std::size_t size, TIter& iter)
542 {
543  details::WriteHelper<>::template write<traits::endian::Big>(value, size, iter);
544 }
545 
553 template <typename T, typename TIter>
554 void writeBig(T value, TIter& iter)
555 {
556  using ValueType = typename std::decay<T>::type;
557  writeBig<sizeof(ValueType)>(static_cast<ValueType>(value), iter);
558 }
559 
570 template <typename T, std::size_t TSize, typename TIter>
571 T readBig(TIter& iter)
572 {
573  return details::ReadHelper<>::template read<traits::endian::Big, T, TSize>(iter);
574 }
575 
584 template <typename T, typename TIter>
585 T readBig(TIter& iter)
586 {
587  using ValueType = typename std::decay<T>::type;
588  return static_cast<T>(readBig<ValueType, sizeof(ValueType)>(iter));
589 }
590 
600 template <typename T, typename TIter>
601 T readBig(TIter& iter, std::size_t size)
602 {
603  return details::ReadHelper<>::template read<traits::endian::Big, T>(size, iter);
604 }
605 
615 template <std::size_t TSize, typename T, typename TIter>
616 void writeLittle(T value, TIter& iter)
617 {
618  details::WriteHelper<>::template write<traits::endian::Little>(value, TSize, iter);
619 }
620 
630 template <typename T, typename TIter>
631 void writeLittle(T value, std::size_t size, TIter& iter)
632 {
633  details::WriteHelper<>::template write<traits::endian::Little>(value, size, iter);
634 }
635 
643 template <typename T, typename TIter>
644 void writeLittle(T value, TIter& iter)
645 {
646  using ValueType = typename std::decay<T>::type;
647  writeLittle<sizeof(ValueType)>(static_cast<ValueType>(value), iter);
648 }
649 
660 template <typename T, std::size_t TSize, typename TIter>
661 T readLittle(TIter& iter)
662 {
663  return details::ReadHelper<>::template read<traits::endian::Little, T, TSize>(iter);
664 }
665 
674 template <typename T, typename TIter>
675 T readLittle(TIter& iter)
676 {
677  using ValueType = typename std::decay<T>::type;
678  return static_cast<T>(readLittle<ValueType, sizeof(ValueType)>(iter));
679 }
680 
690 template <typename T, typename TIter>
691 T readLittle(TIter& iter, std::size_t size)
692 {
693  return details::ReadHelper<>::template read<traits::endian::Little, T>(size, iter);
694 }
695 
697 template <typename T, typename TIter>
699  T value,
700  TIter& iter,
701  const traits::endian::Big& endian)
702 {
703  static_cast<void>(endian);
704  writeBig(value, iter);
705 }
706 
708 template <std::size_t TSize, typename T, typename TIter>
710  T value,
711  TIter& iter,
712  const traits::endian::Big& endian)
713 {
714  static_cast<void>(endian);
715  writeBig<TSize>(value, iter);
716 }
717 
719 template <typename T, typename TIter>
721  T value,
722  std::size_t size,
723  TIter& iter,
724  const traits::endian::Big& endian)
725 {
726  static_cast<void>(endian);
727  writeBig(value, size, iter);
728 }
729 
731 template <typename T, typename TIter>
733  T value,
734  TIter& iter,
735  const traits::endian::Little& endian)
736 {
737  static_cast<void>(endian);
738  writeLittle(value, iter);
739 }
740 
742 template <std::size_t TSize, typename T, typename TIter>
744  T value,
745  TIter& iter,
746  const traits::endian::Little& endian)
747 {
748  static_cast<void>(endian);
749  return writeLittle<TSize>(value, iter);
750 }
751 
753 template <typename T, typename TIter>
755  T value,
756  std::size_t size,
757  TIter& iter,
758  const traits::endian::Little& endian)
759 {
760  static_cast<void>(endian);
761  writeLittle(value, size, iter);
762 }
763 
765 template <typename T, typename TIter>
766 T readData(TIter& iter, const traits::endian::Big& endian)
767 {
768  static_cast<void>(endian);
769  return readBig<T>(iter);
770 }
771 
773 template <typename T, typename TIter>
774 T readData(TIter& iter, std::size_t size, const traits::endian::Big& endian)
775 {
776  static_cast<void>(endian);
777  return readBig<T>(iter, size);
778 }
779 
781 template <typename T, std::size_t TSize, typename TIter>
782 T readData(TIter& iter, const traits::endian::Big& endian)
783 {
784  static_cast<void>(endian);
785  return readBig<T, TSize>(iter);
786 }
787 
789 template <typename T, typename TIter>
790 T readData(TIter& iter, const traits::endian::Little& endian)
791 {
792  static_cast<void>(endian);
793  return readLittle<T>(iter);
794 }
795 
797 template <typename T, std::size_t TSize, typename TIter>
798 T readData(TIter& iter, const traits::endian::Little& endian)
799 {
800  static_cast<void>(endian);
801  return readLittle<T, TSize>(iter);
802 }
803 
805 template <typename T, typename TIter>
806 T readData(TIter& iter, std::size_t size, const traits::endian::Little& endian)
807 {
808  static_cast<void>(endian);
809  return readLittle<T>(iter, size);
810 }
811 
812 
813 } // namespace util
814 
815 } // namespace comms
util::traits::endian::Little Little
Empty class used in traits to indicate Little Endian.
Definition: traits.h:31
util::traits::endian::Big Big
Empty class used in traits to indicate Big Endian.
Definition: traits.h:28
void writeLittle(T value, TIter &iter)
Write part of integral value into the output area using little endian notation.
Definition: access.h:616
T readData(TIter &iter, const traits::endian::Big &endian)
Same as readBig<T, TIter>()
Definition: access.h:766
void writeData(T value, TIter &iter, const traits::endian::Big &endian)
Same as writeBig<T, TIter>()
Definition: access.h:698
T readBig(TIter &iter)
Read part of integral value from the input area using big endian notation.
Definition: access.h:571
void writeBig(T value, TIter &iter)
Write part of integral value into the output area using big endian notation.
Definition: access.h:526
T readLittle(TIter &iter)
Read part of integral value from the input area using little endian notation.
Definition: access.h:661
Main namespace for all classes / functions of COMMS library.
Replacement to std::conditional.
Definition: type_traits.h:28
Empty class used in traits to indicate Big Endian.
Definition: access.h:35
Empty class used in traits to indicate Little Endian.
Definition: access.h:38
Replacement to some types from standard type_traits.