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 LLVM_SUPPORT_ENDIAN_H
15#define LLVM_SUPPORT_ENDIAN_H
16
17#include "llvm/Support/Host.h"
18#include "llvm/Support/SwapByteOrder.h"
19
20namespace llvm {
21namespace support {
22enum endianness {big, little, native};
23
24// These are named values for common alignments.
25enum {aligned = 0, unaligned = 1};
26
27namespace detail {
28  /// \brief ::value is either alignment, or alignof(T) if alignment is 0.
29  template<class T, int alignment>
30  struct PickAlignment {
31    enum { value = alignment == 0 ? alignof(T) : alignment };
32  };
33} // end namespace detail
34
35namespace endian {
36/// Swap the bytes of value to match the given endianness.
37template<typename value_type, endianness endian>
38inline value_type byte_swap(value_type value) {
39  if (endian != native && sys::IsBigEndianHost != (endian == big))
40    sys::swapByteOrder(value);
41  return value;
42}
43
44/// Read a value of a particular endianness from memory.
45template<typename value_type,
46         endianness endian,
47         std::size_t alignment>
48inline value_type read(const void *memory) {
49  value_type ret;
50
51  memcpy(&ret,
52         LLVM_ASSUME_ALIGNED(memory,
53           (detail::PickAlignment<value_type, alignment>::value)),
54         sizeof(value_type));
55  return byte_swap<value_type, endian>(ret);
56}
57
58/// Read a value of a particular endianness from a buffer, and increment the
59/// buffer past that value.
60template<typename value_type, endianness endian, std::size_t alignment,
61         typename CharT>
62inline value_type readNext(const CharT *&memory) {
63  value_type ret = read<value_type, endian, alignment>(memory);
64  memory += sizeof(value_type);
65  return ret;
66}
67
68/// Write a value to memory with a particular endianness.
69template<typename value_type,
70         endianness endian,
71         std::size_t alignment>
72inline void write(void *memory, value_type value) {
73  value = byte_swap<value_type, endian>(value);
74  memcpy(LLVM_ASSUME_ALIGNED(memory,
75           (detail::PickAlignment<value_type, alignment>::value)),
76         &value,
77         sizeof(value_type));
78}
79
80template <typename value_type>
81using make_unsigned_t = typename std::make_unsigned<value_type>::type;
82
83/// Read a value of a particular endianness from memory, for a location
84/// that starts at the given bit offset within the first byte.
85template <typename value_type, endianness endian, std::size_t alignment>
86inline value_type readAtBitAlignment(const void *memory, uint64_t startBit) {
87  assert(startBit < 8);
88  if (startBit == 0)
89    return read<value_type, endian, alignment>(memory);
90  else {
91    // Read two values and compose the result from them.
92    value_type val[2];
93    memcpy(&val[0],
94           LLVM_ASSUME_ALIGNED(
95               memory, (detail::PickAlignment<value_type, alignment>::value)),
96           sizeof(value_type) * 2);
97    val[0] = byte_swap<value_type, endian>(val[0]);
98    val[1] = byte_swap<value_type, endian>(val[1]);
99
100    // Shift bits from the lower value into place.
101    make_unsigned_t<value_type> lowerVal = val[0] >> startBit;
102    // Mask off upper bits after right shift in case of signed type.
103    make_unsigned_t<value_type> numBitsFirstVal =
104        (sizeof(value_type) * 8) - startBit;
105    lowerVal &= ((make_unsigned_t<value_type>)1 << numBitsFirstVal) - 1;
106
107    // Get the bits from the upper value.
108    make_unsigned_t<value_type> upperVal =
109        val[1] & (((make_unsigned_t<value_type>)1 << startBit) - 1);
110    // Shift them in to place.
111    upperVal <<= numBitsFirstVal;
112
113    return lowerVal | upperVal;
114  }
115}
116
117/// Write a value to memory with a particular endianness, for a location
118/// that starts at the given bit offset within the first byte.
119template <typename value_type, endianness endian, std::size_t alignment>
120inline void writeAtBitAlignment(void *memory, value_type value,
121                                uint64_t startBit) {
122  assert(startBit < 8);
123  if (startBit == 0)
124    write<value_type, endian, alignment>(memory, value);
125  else {
126    // Read two values and shift the result into them.
127    value_type val[2];
128    memcpy(&val[0],
129           LLVM_ASSUME_ALIGNED(
130               memory, (detail::PickAlignment<value_type, alignment>::value)),
131           sizeof(value_type) * 2);
132    val[0] = byte_swap<value_type, endian>(val[0]);
133    val[1] = byte_swap<value_type, endian>(val[1]);
134
135    // Mask off any existing bits in the upper part of the lower value that
136    // we want to replace.
137    val[0] &= ((make_unsigned_t<value_type>)1 << startBit) - 1;
138    make_unsigned_t<value_type> numBitsFirstVal =
139        (sizeof(value_type) * 8) - startBit;
140    make_unsigned_t<value_type> lowerVal = value;
141    if (startBit > 0) {
142      // Mask off the upper bits in the new value that are not going to go into
143      // the lower value. This avoids a left shift of a negative value, which
144      // is undefined behavior.
145      lowerVal &= (((make_unsigned_t<value_type>)1 << numBitsFirstVal) - 1);
146      // Now shift the new bits into place
147      lowerVal <<= startBit;
148    }
149    val[0] |= lowerVal;
150
151    // Mask off any existing bits in the lower part of the upper value that
152    // we want to replace.
153    val[1] &= ~(((make_unsigned_t<value_type>)1 << startBit) - 1);
154    // Next shift the bits that go into the upper value into position.
155    make_unsigned_t<value_type> upperVal = value >> numBitsFirstVal;
156    // Mask off upper bits after right shift in case of signed type.
157    upperVal &= ((make_unsigned_t<value_type>)1 << startBit) - 1;
158    val[1] |= upperVal;
159
160    // Finally, rewrite values.
161    val[0] = byte_swap<value_type, endian>(val[0]);
162    val[1] = byte_swap<value_type, endian>(val[1]);
163    memcpy(LLVM_ASSUME_ALIGNED(
164               memory, (detail::PickAlignment<value_type, alignment>::value)),
165           &val[0], sizeof(value_type) * 2);
166  }
167}
168} // end namespace endian
169
170namespace detail {
171template<typename value_type,
172         endianness endian,
173         std::size_t alignment>
174struct packed_endian_specific_integral {
175  packed_endian_specific_integral() = default;
176
177  explicit packed_endian_specific_integral(value_type val) { *this = val; }
178
179  operator value_type() const {
180    return endian::read<value_type, endian, alignment>(
181      (const void*)Value.buffer);
182  }
183
184  void operator=(value_type newValue) {
185    endian::write<value_type, endian, alignment>(
186      (void*)Value.buffer, newValue);
187  }
188
189  packed_endian_specific_integral &operator+=(value_type newValue) {
190    *this = *this + newValue;
191    return *this;
192  }
193
194  packed_endian_specific_integral &operator-=(value_type newValue) {
195    *this = *this - newValue;
196    return *this;
197  }
198
199  packed_endian_specific_integral &operator|=(value_type newValue) {
200    *this = *this | newValue;
201    return *this;
202  }
203
204  packed_endian_specific_integral &operator&=(value_type newValue) {
205    *this = *this & newValue;
206    return *this;
207  }
208
209private:
210  AlignedCharArray<PickAlignment<value_type, alignment>::value,
211                   sizeof(value_type)> Value;
212
213public:
214  struct ref {
215    explicit ref(void *Ptr) : Ptr(Ptr) {}
216
217    operator value_type() const {
218      return endian::read<value_type, endian, alignment>(Ptr);
219    }
220
221    void operator=(value_type NewValue) {
222      endian::write<value_type, endian, alignment>(Ptr, NewValue);
223    }
224
225  private:
226    void *Ptr;
227  };
228};
229
230} // end namespace detail
231
232typedef detail::packed_endian_specific_integral
233                  <uint16_t, little, unaligned> ulittle16_t;
234typedef detail::packed_endian_specific_integral
235                  <uint32_t, little, unaligned> ulittle32_t;
236typedef detail::packed_endian_specific_integral
237                  <uint64_t, little, unaligned> ulittle64_t;
238
239typedef detail::packed_endian_specific_integral
240                   <int16_t, little, unaligned> little16_t;
241typedef detail::packed_endian_specific_integral
242                   <int32_t, little, unaligned> little32_t;
243typedef detail::packed_endian_specific_integral
244                   <int64_t, little, unaligned> little64_t;
245
246typedef detail::packed_endian_specific_integral
247                    <uint16_t, little, aligned> aligned_ulittle16_t;
248typedef detail::packed_endian_specific_integral
249                    <uint32_t, little, aligned> aligned_ulittle32_t;
250typedef detail::packed_endian_specific_integral
251                    <uint64_t, little, aligned> aligned_ulittle64_t;
252
253typedef detail::packed_endian_specific_integral
254                     <int16_t, little, aligned> aligned_little16_t;
255typedef detail::packed_endian_specific_integral
256                     <int32_t, little, aligned> aligned_little32_t;
257typedef detail::packed_endian_specific_integral
258                     <int64_t, little, aligned> aligned_little64_t;
259
260typedef detail::packed_endian_specific_integral
261                  <uint16_t, big, unaligned>    ubig16_t;
262typedef detail::packed_endian_specific_integral
263                  <uint32_t, big, unaligned>    ubig32_t;
264typedef detail::packed_endian_specific_integral
265                  <uint64_t, big, unaligned>    ubig64_t;
266
267typedef detail::packed_endian_specific_integral
268                   <int16_t, big, unaligned>    big16_t;
269typedef detail::packed_endian_specific_integral
270                   <int32_t, big, unaligned>    big32_t;
271typedef detail::packed_endian_specific_integral
272                   <int64_t, big, unaligned>    big64_t;
273
274typedef detail::packed_endian_specific_integral
275                    <uint16_t, big, aligned>    aligned_ubig16_t;
276typedef detail::packed_endian_specific_integral
277                    <uint32_t, big, aligned>    aligned_ubig32_t;
278typedef detail::packed_endian_specific_integral
279                    <uint64_t, big, aligned>    aligned_ubig64_t;
280
281typedef detail::packed_endian_specific_integral
282                     <int16_t, big, aligned>    aligned_big16_t;
283typedef detail::packed_endian_specific_integral
284                     <int32_t, big, aligned>    aligned_big32_t;
285typedef detail::packed_endian_specific_integral
286                     <int64_t, big, aligned>    aligned_big64_t;
287
288typedef detail::packed_endian_specific_integral
289                  <uint16_t, native, unaligned> unaligned_uint16_t;
290typedef detail::packed_endian_specific_integral
291                  <uint32_t, native, unaligned> unaligned_uint32_t;
292typedef detail::packed_endian_specific_integral
293                  <uint64_t, native, unaligned> unaligned_uint64_t;
294
295typedef detail::packed_endian_specific_integral
296                   <int16_t, native, unaligned> unaligned_int16_t;
297typedef detail::packed_endian_specific_integral
298                   <int32_t, native, unaligned> unaligned_int32_t;
299typedef detail::packed_endian_specific_integral
300                   <int64_t, native, unaligned> unaligned_int64_t;
301
302namespace endian {
303template <typename T, endianness E> inline T read(const void *P) {
304  return *(const detail::packed_endian_specific_integral<T, E, unaligned> *)P;
305}
306
307template <endianness E> inline uint16_t read16(const void *P) {
308  return read<uint16_t, E>(P);
309}
310template <endianness E> inline uint32_t read32(const void *P) {
311  return read<uint32_t, E>(P);
312}
313template <endianness E> inline uint64_t read64(const void *P) {
314  return read<uint64_t, E>(P);
315}
316
317inline uint16_t read16le(const void *P) { return read16<little>(P); }
318inline uint32_t read32le(const void *P) { return read32<little>(P); }
319inline uint64_t read64le(const void *P) { return read64<little>(P); }
320inline uint16_t read16be(const void *P) { return read16<big>(P); }
321inline uint32_t read32be(const void *P) { return read32<big>(P); }
322inline uint64_t read64be(const void *P) { return read64<big>(P); }
323
324template <typename T, endianness E> inline void write(void *P, T V) {
325  *(detail::packed_endian_specific_integral<T, E, unaligned> *)P = V;
326}
327
328template <endianness E> inline void write16(void *P, uint16_t V) {
329  write<uint16_t, E>(P, V);
330}
331template <endianness E> inline void write32(void *P, uint32_t V) {
332  write<uint32_t, E>(P, V);
333}
334template <endianness E> inline void write64(void *P, uint64_t V) {
335  write<uint64_t, E>(P, V);
336}
337
338inline void write16le(void *P, uint16_t V) { write16<little>(P, V); }
339inline void write32le(void *P, uint32_t V) { write32<little>(P, V); }
340inline void write64le(void *P, uint64_t V) { write64<little>(P, V); }
341inline void write16be(void *P, uint16_t V) { write16<big>(P, V); }
342inline void write32be(void *P, uint32_t V) { write32<big>(P, V); }
343inline void write64be(void *P, uint64_t V) { write64<big>(P, V); }
344} // end namespace endian
345} // end namespace support
346} // end namespace llvm
347
348#endif
349