WPILibC++  2020.3.2
Endian.h
1 //===- Endian.h - Utilities for IO with endian specific data ----*- C++ -*-===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file declares generic functions to read and write endian specific data.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef WPIUTIL_WPI_ENDIAN_H
15 #define WPIUTIL_WPI_ENDIAN_H
16 
17 #include "wpi/AlignOf.h"
18 #include "wpi/Compiler.h"
19 #include "wpi/SwapByteOrder.h"
20 
21 #if defined(__linux__) || defined(__GNU__)
22 #include <endian.h>
23 #endif
24 
25 #include <cassert>
26 #include <cstddef>
27 #include <cstdint>
28 #include <cstring>
29 #include <type_traits>
30 
31 namespace wpi {
32 namespace support {
33 
34 enum endianness {big, little, native};
35 
36 // These are named values for common alignments.
37 enum {aligned = 0, unaligned = 1};
38 
39 namespace detail {
40 
42 template<class T, int alignment>
43 struct PickAlignment {
44  enum { value = alignment == 0 ? alignof(T) : alignment };
45 };
46 
47 } // end namespace detail
48 
49 namespace endian {
50 
51 constexpr endianness system_endianness() {
52 #ifdef _WIN32
53  return little;
54 #elif defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && __BYTE_ORDER == __BIG_ENDIAN
55  return big;
56 #else
57  return little;
58 #endif
59 }
60 
61 template <typename value_type>
62 inline value_type byte_swap(value_type value, endianness endian) {
63  if ((endian != native) && (endian != system_endianness()))
64  sys::swapByteOrder(value);
65  return value;
66 }
67 
69 template<typename value_type, endianness endian>
70 inline value_type byte_swap(value_type value) {
71  return byte_swap(value, endian);
72 }
73 
75 template <typename value_type, std::size_t alignment>
76 inline value_type read(const void *memory, endianness endian) {
77  value_type ret;
78 
79  memcpy(&ret,
80  LLVM_ASSUME_ALIGNED(
81  memory, (detail::PickAlignment<value_type, alignment>::value)),
82  sizeof(value_type));
83  return byte_swap<value_type>(ret, endian);
84 }
85 
86 template<typename value_type,
87  endianness endian,
88  std::size_t alignment>
89 inline value_type read(const void *memory) {
90  return read<value_type, alignment>(memory, endian);
91 }
92 
95 template <typename value_type, std::size_t alignment, typename CharT>
96 inline value_type readNext(const CharT *&memory, endianness endian) {
97  value_type ret = read<value_type, alignment>(memory, endian);
98  memory += sizeof(value_type);
99  return ret;
100 }
101 
102 template<typename value_type, endianness endian, std::size_t alignment,
103  typename CharT>
104 inline value_type readNext(const CharT *&memory) {
105  return readNext<value_type, alignment, CharT>(memory, endian);
106 }
107 
109 template <typename value_type, std::size_t alignment>
110 inline void write(void *memory, value_type value, endianness endian) {
111  value = byte_swap<value_type>(value, endian);
112  memcpy(LLVM_ASSUME_ALIGNED(
113  memory, (detail::PickAlignment<value_type, alignment>::value)),
114  &value, sizeof(value_type));
115 }
116 
117 template<typename value_type,
118  endianness endian,
119  std::size_t alignment>
120 inline void write(void *memory, value_type value) {
121  write<value_type, alignment>(memory, value, endian);
122 }
123 
124 template <typename value_type>
125 using make_unsigned_t = typename std::make_unsigned<value_type>::type;
126 
129 template <typename value_type, endianness endian, std::size_t alignment>
130 inline value_type readAtBitAlignment(const void *memory, uint64_t startBit) {
131  assert(startBit < 8);
132  if (startBit == 0)
133  return read<value_type, endian, alignment>(memory);
134  else {
135  // Read two values and compose the result from them.
136  value_type val[2];
137  memcpy(&val[0],
138  LLVM_ASSUME_ALIGNED(
139  memory, (detail::PickAlignment<value_type, alignment>::value)),
140  sizeof(value_type) * 2);
141  val[0] = byte_swap<value_type, endian>(val[0]);
142  val[1] = byte_swap<value_type, endian>(val[1]);
143 
144  // Shift bits from the lower value into place.
145  make_unsigned_t<value_type> lowerVal = val[0] >> startBit;
146  // Mask off upper bits after right shift in case of signed type.
147  make_unsigned_t<value_type> numBitsFirstVal =
148  (sizeof(value_type) * 8) - startBit;
149  lowerVal &= ((make_unsigned_t<value_type>)1 << numBitsFirstVal) - 1;
150 
151  // Get the bits from the upper value.
152  make_unsigned_t<value_type> upperVal =
153  val[1] & (((make_unsigned_t<value_type>)1 << startBit) - 1);
154  // Shift them in to place.
155  upperVal <<= numBitsFirstVal;
156 
157  return lowerVal | upperVal;
158  }
159 }
160 
163 template <typename value_type, endianness endian, std::size_t alignment>
164 inline void writeAtBitAlignment(void *memory, value_type value,
165  uint64_t startBit) {
166  assert(startBit < 8);
167  if (startBit == 0)
168  write<value_type, endian, alignment>(memory, value);
169  else {
170  // Read two values and shift the result into them.
171  value_type val[2];
172  memcpy(&val[0],
173  LLVM_ASSUME_ALIGNED(
174  memory, (detail::PickAlignment<value_type, alignment>::value)),
175  sizeof(value_type) * 2);
176  val[0] = byte_swap<value_type, endian>(val[0]);
177  val[1] = byte_swap<value_type, endian>(val[1]);
178 
179  // Mask off any existing bits in the upper part of the lower value that
180  // we want to replace.
181  val[0] &= ((make_unsigned_t<value_type>)1 << startBit) - 1;
182  make_unsigned_t<value_type> numBitsFirstVal =
183  (sizeof(value_type) * 8) - startBit;
184  make_unsigned_t<value_type> lowerVal = value;
185  if (startBit > 0) {
186  // Mask off the upper bits in the new value that are not going to go into
187  // the lower value. This avoids a left shift of a negative value, which
188  // is undefined behavior.
189  lowerVal &= (((make_unsigned_t<value_type>)1 << numBitsFirstVal) - 1);
190  // Now shift the new bits into place
191  lowerVal <<= startBit;
192  }
193  val[0] |= lowerVal;
194 
195  // Mask off any existing bits in the lower part of the upper value that
196  // we want to replace.
197  val[1] &= ~(((make_unsigned_t<value_type>)1 << startBit) - 1);
198  // Next shift the bits that go into the upper value into position.
199  make_unsigned_t<value_type> upperVal = value >> numBitsFirstVal;
200  // Mask off upper bits after right shift in case of signed type.
201  upperVal &= ((make_unsigned_t<value_type>)1 << startBit) - 1;
202  val[1] |= upperVal;
203 
204  // Finally, rewrite values.
205  val[0] = byte_swap<value_type, endian>(val[0]);
206  val[1] = byte_swap<value_type, endian>(val[1]);
207  memcpy(LLVM_ASSUME_ALIGNED(
208  memory, (detail::PickAlignment<value_type, alignment>::value)),
209  &val[0], sizeof(value_type) * 2);
210  }
211 }
212 
213 } // end namespace endian
214 
215 namespace detail {
216 
217 template<typename value_type,
218  endianness endian,
219  std::size_t alignment>
222 
223  explicit packed_endian_specific_integral(value_type val) { *this = val; }
224 
225  operator value_type() const {
226  return endian::read<value_type, endian, alignment>(
227  (const void*)Value.buffer);
228  }
229 
230  void operator=(value_type newValue) {
231  endian::write<value_type, endian, alignment>(
232  (void*)Value.buffer, newValue);
233  }
234 
235  packed_endian_specific_integral &operator+=(value_type newValue) {
236  *this = *this + newValue;
237  return *this;
238  }
239 
240  packed_endian_specific_integral &operator-=(value_type newValue) {
241  *this = *this - newValue;
242  return *this;
243  }
244 
245  packed_endian_specific_integral &operator|=(value_type newValue) {
246  *this = *this | newValue;
247  return *this;
248  }
249 
250  packed_endian_specific_integral &operator&=(value_type newValue) {
251  *this = *this & newValue;
252  return *this;
253  }
254 
255 private:
257  sizeof(value_type)> Value;
258 
259 public:
260  struct ref {
261  explicit ref(void *Ptr) : Ptr(Ptr) {}
262 
263  operator value_type() const {
264  return endian::read<value_type, endian, alignment>(Ptr);
265  }
266 
267  void operator=(value_type NewValue) {
268  endian::write<value_type, endian, alignment>(Ptr, NewValue);
269  }
270 
271  private:
272  void *Ptr;
273  };
274 };
275 
276 } // end namespace detail
277 
278 using ulittle16_t =
280 using ulittle32_t =
282 using ulittle64_t =
284 
285 using little16_t =
287 using little32_t =
289 using little64_t =
291 
292 using aligned_ulittle16_t =
294 using aligned_ulittle32_t =
296 using aligned_ulittle64_t =
298 
299 using aligned_little16_t =
301 using aligned_little32_t =
303 using aligned_little64_t =
305 
306 using ubig16_t =
308 using ubig32_t =
310 using ubig64_t =
312 
313 using big16_t =
315 using big32_t =
317 using big64_t =
319 
320 using aligned_ubig16_t =
322 using aligned_ubig32_t =
324 using aligned_ubig64_t =
326 
327 using aligned_big16_t =
329 using aligned_big32_t =
331 using aligned_big64_t =
333 
334 using unaligned_uint16_t =
336 using unaligned_uint32_t =
338 using unaligned_uint64_t =
340 
341 using unaligned_int16_t =
343 using unaligned_int32_t =
345 using unaligned_int64_t =
347 
348 namespace endian {
349 
350 template <typename T> inline T read(const void *P, endianness E) {
351  return read<T, unaligned>(P, E);
352 }
353 
354 template <typename T, endianness E> inline T read(const void *P) {
355  return *(const detail::packed_endian_specific_integral<T, E, unaligned> *)P;
356 }
357 
358 inline uint16_t read16(const void *P, endianness E) {
359  return read<uint16_t>(P, E);
360 }
361 inline uint32_t read32(const void *P, endianness E) {
362  return read<uint32_t>(P, E);
363 }
364 inline uint64_t read64(const void *P, endianness E) {
365  return read<uint64_t>(P, E);
366 }
367 
368 template <endianness E> inline uint16_t read16(const void *P) {
369  return read<uint16_t, E>(P);
370 }
371 template <endianness E> inline uint32_t read32(const void *P) {
372  return read<uint32_t, E>(P);
373 }
374 template <endianness E> inline uint64_t read64(const void *P) {
375  return read<uint64_t, E>(P);
376 }
377 
378 inline uint16_t read16le(const void *P) { return read16<little>(P); }
379 inline uint32_t read32le(const void *P) { return read32<little>(P); }
380 inline uint64_t read64le(const void *P) { return read64<little>(P); }
381 inline uint16_t read16be(const void *P) { return read16<big>(P); }
382 inline uint32_t read32be(const void *P) { return read32<big>(P); }
383 inline uint64_t read64be(const void *P) { return read64<big>(P); }
384 
385 template <typename T> inline void write(void *P, T V, endianness E) {
386  write<T, unaligned>(P, V, E);
387 }
388 
389 template <typename T, endianness E> inline void write(void *P, T V) {
390  *(detail::packed_endian_specific_integral<T, E, unaligned> *)P = V;
391 }
392 
393 inline void write16(void *P, uint16_t V, endianness E) {
394  write<uint16_t>(P, V, E);
395 }
396 inline void write32(void *P, uint32_t V, endianness E) {
397  write<uint32_t>(P, V, E);
398 }
399 inline void write64(void *P, uint64_t V, endianness E) {
400  write<uint64_t>(P, V, E);
401 }
402 
403 template <endianness E> inline void write16(void *P, uint16_t V) {
404  write<uint16_t, E>(P, V);
405 }
406 template <endianness E> inline void write32(void *P, uint32_t V) {
407  write<uint32_t, E>(P, V);
408 }
409 template <endianness E> inline void write64(void *P, uint64_t V) {
410  write<uint64_t, E>(P, V);
411 }
412 
413 inline void write16le(void *P, uint16_t V) { write16<little>(P, V); }
414 inline void write32le(void *P, uint32_t V) { write32<little>(P, V); }
415 inline void write64le(void *P, uint64_t V) { write64<little>(P, V); }
416 inline void write16be(void *P, uint16_t V) { write16<big>(P, V); }
417 inline void write32be(void *P, uint32_t V) { write32<big>(P, V); }
418 inline void write64be(void *P, uint64_t V) { write64<big>(P, V); }
419 
420 } // end namespace endian
421 
422 } // end namespace support
423 } // end namespace wpi
424 
425 #endif // WPIUTIL_WPI_ENDIAN_H
wpi::support::detail::packed_endian_specific_integral
Definition: Endian.h:220
wpi::support::detail::PickAlignment
::value is either alignment, or alignof(T) if alignment is 0.
Definition: Endian.h:43
wpi::AlignedCharArray
Definition: AlignOf.h:36
wpi::support::detail::packed_endian_specific_integral::ref
Definition: Endian.h:260
wpi
WPILib C++ utilities (wpiutil) namespace.
Definition: EventLoopRunner.h:17