1 //===- Endian.h - Utilities for IO with endian specific data ----*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file declares generic functions to read and write endian specific data.
11 //===----------------------------------------------------------------------===//
13 #ifndef LLVM_SUPPORT_ENDIAN_H
14 #define LLVM_SUPPORT_ENDIAN_H
16 #include "llvm/Support/AlignOf.h"
17 #include "llvm/Support/Compiler.h"
18 #include "llvm/Support/Host.h"
19 #include "llvm/Support/SwapByteOrder.h"
24 #include <type_traits>
29 enum endianness
{big
, little
, native
};
31 // These are named values for common alignments.
32 enum {aligned
= 0, unaligned
= 1};
36 /// ::value is either alignment, or alignof(T) if alignment is 0.
37 template<class T
, int alignment
>
38 struct PickAlignment
{
39 enum { value
= alignment
== 0 ? alignof(T
) : alignment
};
42 } // end namespace detail
46 constexpr endianness
system_endianness() {
47 return sys::IsBigEndianHost
? big
: little
;
50 template <typename value_type
>
51 inline value_type
byte_swap(value_type value
, endianness endian
) {
52 if ((endian
!= native
) && (endian
!= system_endianness()))
53 sys::swapByteOrder(value
);
57 /// Swap the bytes of value to match the given endianness.
58 template<typename value_type
, endianness endian
>
59 inline value_type
byte_swap(value_type value
) {
60 return byte_swap(value
, endian
);
63 /// Read a value of a particular endianness from memory.
64 template <typename value_type
, std::size_t alignment
>
65 inline value_type
read(const void *memory
, endianness endian
) {
70 memory
, (detail::PickAlignment
<value_type
, alignment
>::value
)),
72 return byte_swap
<value_type
>(ret
, endian
);
75 template<typename value_type
,
77 std::size_t alignment
>
78 inline value_type
read(const void *memory
) {
79 return read
<value_type
, alignment
>(memory
, endian
);
82 /// Read a value of a particular endianness from a buffer, and increment the
83 /// buffer past that value.
84 template <typename value_type
, std::size_t alignment
, typename CharT
>
85 inline value_type
readNext(const CharT
*&memory
, endianness endian
) {
86 value_type ret
= read
<value_type
, alignment
>(memory
, endian
);
87 memory
+= sizeof(value_type
);
91 template<typename value_type
, endianness endian
, std::size_t alignment
,
93 inline value_type
readNext(const CharT
*&memory
) {
94 return readNext
<value_type
, alignment
, CharT
>(memory
, endian
);
97 /// Write a value to memory with a particular endianness.
98 template <typename value_type
, std::size_t alignment
>
99 inline void write(void *memory
, value_type value
, endianness endian
) {
100 value
= byte_swap
<value_type
>(value
, endian
);
101 memcpy(LLVM_ASSUME_ALIGNED(
102 memory
, (detail::PickAlignment
<value_type
, alignment
>::value
)),
103 &value
, sizeof(value_type
));
106 template<typename value_type
,
108 std::size_t alignment
>
109 inline void write(void *memory
, value_type value
) {
110 write
<value_type
, alignment
>(memory
, value
, endian
);
113 template <typename value_type
>
114 using make_unsigned_t
= typename
std::make_unsigned
<value_type
>::type
;
116 /// Read a value of a particular endianness from memory, for a location
117 /// that starts at the given bit offset within the first byte.
118 template <typename value_type
, endianness endian
, std::size_t alignment
>
119 inline value_type
readAtBitAlignment(const void *memory
, uint64_t startBit
) {
120 assert(startBit
< 8);
122 return read
<value_type
, endian
, alignment
>(memory
);
124 // Read two values and compose the result from them.
128 memory
, (detail::PickAlignment
<value_type
, alignment
>::value
)),
129 sizeof(value_type
) * 2);
130 val
[0] = byte_swap
<value_type
, endian
>(val
[0]);
131 val
[1] = byte_swap
<value_type
, endian
>(val
[1]);
133 // Shift bits from the lower value into place.
134 make_unsigned_t
<value_type
> lowerVal
= val
[0] >> startBit
;
135 // Mask off upper bits after right shift in case of signed type.
136 make_unsigned_t
<value_type
> numBitsFirstVal
=
137 (sizeof(value_type
) * 8) - startBit
;
138 lowerVal
&= ((make_unsigned_t
<value_type
>)1 << numBitsFirstVal
) - 1;
140 // Get the bits from the upper value.
141 make_unsigned_t
<value_type
> upperVal
=
142 val
[1] & (((make_unsigned_t
<value_type
>)1 << startBit
) - 1);
143 // Shift them in to place.
144 upperVal
<<= numBitsFirstVal
;
146 return lowerVal
| upperVal
;
150 /// Write a value to memory with a particular endianness, for a location
151 /// that starts at the given bit offset within the first byte.
152 template <typename value_type
, endianness endian
, std::size_t alignment
>
153 inline void writeAtBitAlignment(void *memory
, value_type value
,
155 assert(startBit
< 8);
157 write
<value_type
, endian
, alignment
>(memory
, value
);
159 // Read two values and shift the result into them.
163 memory
, (detail::PickAlignment
<value_type
, alignment
>::value
)),
164 sizeof(value_type
) * 2);
165 val
[0] = byte_swap
<value_type
, endian
>(val
[0]);
166 val
[1] = byte_swap
<value_type
, endian
>(val
[1]);
168 // Mask off any existing bits in the upper part of the lower value that
169 // we want to replace.
170 val
[0] &= ((make_unsigned_t
<value_type
>)1 << startBit
) - 1;
171 make_unsigned_t
<value_type
> numBitsFirstVal
=
172 (sizeof(value_type
) * 8) - startBit
;
173 make_unsigned_t
<value_type
> lowerVal
= value
;
175 // Mask off the upper bits in the new value that are not going to go into
176 // the lower value. This avoids a left shift of a negative value, which
177 // is undefined behavior.
178 lowerVal
&= (((make_unsigned_t
<value_type
>)1 << numBitsFirstVal
) - 1);
179 // Now shift the new bits into place
180 lowerVal
<<= startBit
;
184 // Mask off any existing bits in the lower part of the upper value that
185 // we want to replace.
186 val
[1] &= ~(((make_unsigned_t
<value_type
>)1 << startBit
) - 1);
187 // Next shift the bits that go into the upper value into position.
188 make_unsigned_t
<value_type
> upperVal
= value
>> numBitsFirstVal
;
189 // Mask off upper bits after right shift in case of signed type.
190 upperVal
&= ((make_unsigned_t
<value_type
>)1 << startBit
) - 1;
193 // Finally, rewrite values.
194 val
[0] = byte_swap
<value_type
, endian
>(val
[0]);
195 val
[1] = byte_swap
<value_type
, endian
>(val
[1]);
196 memcpy(LLVM_ASSUME_ALIGNED(
197 memory
, (detail::PickAlignment
<value_type
, alignment
>::value
)),
198 &val
[0], sizeof(value_type
) * 2);
202 } // end namespace endian
206 template <typename ValueType
, endianness Endian
, std::size_t Alignment
,
207 std::size_t ALIGN
= PickAlignment
<ValueType
, Alignment
>::value
>
208 struct packed_endian_specific_integral
{
209 using value_type
= ValueType
;
210 static constexpr endianness endian
= Endian
;
211 static constexpr std::size_t alignment
= Alignment
;
213 packed_endian_specific_integral() = default;
215 explicit packed_endian_specific_integral(value_type val
) { *this = val
; }
217 operator value_type() const {
218 return endian::read
<value_type
, endian
, alignment
>(
219 (const void*)Value
.buffer
);
222 void operator=(value_type newValue
) {
223 endian::write
<value_type
, endian
, alignment
>(
224 (void*)Value
.buffer
, newValue
);
227 packed_endian_specific_integral
&operator+=(value_type newValue
) {
228 *this = *this + newValue
;
232 packed_endian_specific_integral
&operator-=(value_type newValue
) {
233 *this = *this - newValue
;
237 packed_endian_specific_integral
&operator|=(value_type newValue
) {
238 *this = *this | newValue
;
242 packed_endian_specific_integral
&operator&=(value_type newValue
) {
243 *this = *this & newValue
;
249 alignas(ALIGN
) char buffer
[sizeof(value_type
)];
254 explicit ref(void *Ptr
) : Ptr(Ptr
) {}
256 operator value_type() const {
257 return endian::read
<value_type
, endian
, alignment
>(Ptr
);
260 void operator=(value_type NewValue
) {
261 endian::write
<value_type
, endian
, alignment
>(Ptr
, NewValue
);
269 } // end namespace detail
272 detail::packed_endian_specific_integral
<uint16_t, little
, unaligned
>;
274 detail::packed_endian_specific_integral
<uint32_t, little
, unaligned
>;
276 detail::packed_endian_specific_integral
<uint64_t, little
, unaligned
>;
279 detail::packed_endian_specific_integral
<int16_t, little
, unaligned
>;
281 detail::packed_endian_specific_integral
<int32_t, little
, unaligned
>;
283 detail::packed_endian_specific_integral
<int64_t, little
, unaligned
>;
285 using aligned_ulittle16_t
=
286 detail::packed_endian_specific_integral
<uint16_t, little
, aligned
>;
287 using aligned_ulittle32_t
=
288 detail::packed_endian_specific_integral
<uint32_t, little
, aligned
>;
289 using aligned_ulittle64_t
=
290 detail::packed_endian_specific_integral
<uint64_t, little
, aligned
>;
292 using aligned_little16_t
=
293 detail::packed_endian_specific_integral
<int16_t, little
, aligned
>;
294 using aligned_little32_t
=
295 detail::packed_endian_specific_integral
<int32_t, little
, aligned
>;
296 using aligned_little64_t
=
297 detail::packed_endian_specific_integral
<int64_t, little
, aligned
>;
300 detail::packed_endian_specific_integral
<uint16_t, big
, unaligned
>;
302 detail::packed_endian_specific_integral
<uint32_t, big
, unaligned
>;
304 detail::packed_endian_specific_integral
<uint64_t, big
, unaligned
>;
307 detail::packed_endian_specific_integral
<int16_t, big
, unaligned
>;
309 detail::packed_endian_specific_integral
<int32_t, big
, unaligned
>;
311 detail::packed_endian_specific_integral
<int64_t, big
, unaligned
>;
313 using aligned_ubig16_t
=
314 detail::packed_endian_specific_integral
<uint16_t, big
, aligned
>;
315 using aligned_ubig32_t
=
316 detail::packed_endian_specific_integral
<uint32_t, big
, aligned
>;
317 using aligned_ubig64_t
=
318 detail::packed_endian_specific_integral
<uint64_t, big
, aligned
>;
320 using aligned_big16_t
=
321 detail::packed_endian_specific_integral
<int16_t, big
, aligned
>;
322 using aligned_big32_t
=
323 detail::packed_endian_specific_integral
<int32_t, big
, aligned
>;
324 using aligned_big64_t
=
325 detail::packed_endian_specific_integral
<int64_t, big
, aligned
>;
327 using unaligned_uint16_t
=
328 detail::packed_endian_specific_integral
<uint16_t, native
, unaligned
>;
329 using unaligned_uint32_t
=
330 detail::packed_endian_specific_integral
<uint32_t, native
, unaligned
>;
331 using unaligned_uint64_t
=
332 detail::packed_endian_specific_integral
<uint64_t, native
, unaligned
>;
334 using unaligned_int16_t
=
335 detail::packed_endian_specific_integral
<int16_t, native
, unaligned
>;
336 using unaligned_int32_t
=
337 detail::packed_endian_specific_integral
<int32_t, native
, unaligned
>;
338 using unaligned_int64_t
=
339 detail::packed_endian_specific_integral
<int64_t, native
, unaligned
>;
341 template <typename T
>
342 using little_t
= detail::packed_endian_specific_integral
<T
, little
, unaligned
>;
343 template <typename T
>
344 using big_t
= detail::packed_endian_specific_integral
<T
, big
, unaligned
>;
346 template <typename T
>
347 using aligned_little_t
=
348 detail::packed_endian_specific_integral
<T
, little
, aligned
>;
349 template <typename T
>
350 using aligned_big_t
= detail::packed_endian_specific_integral
<T
, big
, aligned
>;
354 template <typename T
> inline T
read(const void *P
, endianness E
) {
355 return read
<T
, unaligned
>(P
, E
);
358 template <typename T
, endianness E
> inline T
read(const void *P
) {
359 return *(const detail::packed_endian_specific_integral
<T
, E
, unaligned
> *)P
;
362 inline uint16_t read16(const void *P
, endianness E
) {
363 return read
<uint16_t>(P
, E
);
365 inline uint32_t read32(const void *P
, endianness E
) {
366 return read
<uint32_t>(P
, E
);
368 inline uint64_t read64(const void *P
, endianness E
) {
369 return read
<uint64_t>(P
, E
);
372 template <endianness E
> inline uint16_t read16(const void *P
) {
373 return read
<uint16_t, E
>(P
);
375 template <endianness E
> inline uint32_t read32(const void *P
) {
376 return read
<uint32_t, E
>(P
);
378 template <endianness E
> inline uint64_t read64(const void *P
) {
379 return read
<uint64_t, E
>(P
);
382 inline uint16_t read16le(const void *P
) { return read16
<little
>(P
); }
383 inline uint32_t read32le(const void *P
) { return read32
<little
>(P
); }
384 inline uint64_t read64le(const void *P
) { return read64
<little
>(P
); }
385 inline uint16_t read16be(const void *P
) { return read16
<big
>(P
); }
386 inline uint32_t read32be(const void *P
) { return read32
<big
>(P
); }
387 inline uint64_t read64be(const void *P
) { return read64
<big
>(P
); }
389 template <typename T
> inline void write(void *P
, T V
, endianness E
) {
390 write
<T
, unaligned
>(P
, V
, E
);
393 template <typename T
, endianness E
> inline void write(void *P
, T V
) {
394 *(detail::packed_endian_specific_integral
<T
, E
, unaligned
> *)P
= V
;
397 inline void write16(void *P
, uint16_t V
, endianness E
) {
398 write
<uint16_t>(P
, V
, E
);
400 inline void write32(void *P
, uint32_t V
, endianness E
) {
401 write
<uint32_t>(P
, V
, E
);
403 inline void write64(void *P
, uint64_t V
, endianness E
) {
404 write
<uint64_t>(P
, V
, E
);
407 template <endianness E
> inline void write16(void *P
, uint16_t V
) {
408 write
<uint16_t, E
>(P
, V
);
410 template <endianness E
> inline void write32(void *P
, uint32_t V
) {
411 write
<uint32_t, E
>(P
, V
);
413 template <endianness E
> inline void write64(void *P
, uint64_t V
) {
414 write
<uint64_t, E
>(P
, V
);
417 inline void write16le(void *P
, uint16_t V
) { write16
<little
>(P
, V
); }
418 inline void write32le(void *P
, uint32_t V
) { write32
<little
>(P
, V
); }
419 inline void write64le(void *P
, uint64_t V
) { write64
<little
>(P
, V
); }
420 inline void write16be(void *P
, uint16_t V
) { write16
<big
>(P
, V
); }
421 inline void write32be(void *P
, uint32_t V
) { write32
<big
>(P
, V
); }
422 inline void write64be(void *P
, uint64_t V
) { write64
<big
>(P
, V
); }
424 } // end namespace endian
426 } // end namespace support
427 } // end namespace llvm
429 #endif // LLVM_SUPPORT_ENDIAN_H