- //===- Endian.h - Utilities for IO with endian specific data ----*- C++ -*-===// 
- // 
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 
- // See https://llvm.org/LICENSE.txt for license information. 
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 
- // 
- //===----------------------------------------------------------------------===// 
- // 
- // This file declares generic functions to read and write endian specific data. 
- // 
- //===----------------------------------------------------------------------===// 
-   
- #ifndef LLVM_SUPPORT_ENDIAN_H 
- #define LLVM_SUPPORT_ENDIAN_H 
-   
- #include "llvm/Support/Compiler.h" 
- #include "llvm/Support/SwapByteOrder.h" 
- #include <cassert> 
- #include <cstddef> 
- #include <cstdint> 
- #include <cstring> 
- #include <type_traits> 
-   
- namespace llvm { 
- namespace support { 
-   
- enum endianness {big, little, native}; 
-   
- // These are named values for common alignments. 
- enum {aligned = 0, unaligned = 1}; 
-   
- namespace detail { 
-   
- /// ::value is either alignment, or alignof(T) if alignment is 0. 
- template<class T, int alignment> 
- struct PickAlignment { 
-  enum { value = alignment == 0 ? alignof(T) : alignment }; 
- }; 
-   
- } // end namespace detail 
-   
- namespace endian { 
-   
- constexpr endianness system_endianness() { 
-   return sys::IsBigEndianHost ? big : little; 
- } 
-   
- template <typename value_type> 
- inline value_type byte_swap(value_type value, endianness endian) { 
-   if ((endian != native) && (endian != system_endianness())) 
-     sys::swapByteOrder(value); 
-   return value; 
- } 
-   
- /// Swap the bytes of value to match the given endianness. 
- template<typename value_type, endianness endian> 
- inline value_type byte_swap(value_type value) { 
-   return byte_swap(value, endian); 
- } 
-   
- /// Read a value of a particular endianness from memory. 
- template <typename value_type, std::size_t alignment> 
- inline value_type read(const void *memory, endianness endian) { 
-   value_type ret; 
-   
-   memcpy(&ret, 
-          LLVM_ASSUME_ALIGNED( 
-              memory, (detail::PickAlignment<value_type, alignment>::value)), 
-          sizeof(value_type)); 
-   return byte_swap<value_type>(ret, endian); 
- } 
-   
- template<typename value_type, 
-          endianness endian, 
-          std::size_t alignment> 
- inline value_type read(const void *memory) { 
-   return read<value_type, alignment>(memory, endian); 
- } 
-   
- /// Read a value of a particular endianness from a buffer, and increment the 
- /// buffer past that value. 
- template <typename value_type, std::size_t alignment, typename CharT> 
- inline value_type readNext(const CharT *&memory, endianness endian) { 
-   value_type ret = read<value_type, alignment>(memory, endian); 
-   memory += sizeof(value_type); 
-   return ret; 
- } 
-   
- template<typename value_type, endianness endian, std::size_t alignment, 
-          typename CharT> 
- inline value_type readNext(const CharT *&memory) { 
-   return readNext<value_type, alignment, CharT>(memory, endian); 
- } 
-   
- /// Write a value to memory with a particular endianness. 
- template <typename value_type, std::size_t alignment> 
- inline void write(void *memory, value_type value, endianness endian) { 
-   value = byte_swap<value_type>(value, endian); 
-   memcpy(LLVM_ASSUME_ALIGNED( 
-              memory, (detail::PickAlignment<value_type, alignment>::value)), 
-          &value, sizeof(value_type)); 
- } 
-   
- template<typename value_type, 
-          endianness endian, 
-          std::size_t alignment> 
- inline void write(void *memory, value_type value) { 
-   write<value_type, alignment>(memory, value, endian); 
- } 
-   
- template <typename value_type> 
- using make_unsigned_t = std::make_unsigned_t<value_type>; 
-   
- /// Read a value of a particular endianness from memory, for a location 
- /// that starts at the given bit offset within the first byte. 
- template <typename value_type, endianness endian, std::size_t alignment> 
- inline value_type readAtBitAlignment(const void *memory, uint64_t startBit) { 
-   assert(startBit < 8); 
-   if (startBit == 0) 
-     return read<value_type, endian, alignment>(memory); 
-   else { 
-     // Read two values and compose the result from them. 
-     value_type val[2]; 
-     memcpy(&val[0], 
-            LLVM_ASSUME_ALIGNED( 
-                memory, (detail::PickAlignment<value_type, alignment>::value)), 
-            sizeof(value_type) * 2); 
-     val[0] = byte_swap<value_type, endian>(val[0]); 
-     val[1] = byte_swap<value_type, endian>(val[1]); 
-   
-     // Shift bits from the lower value into place. 
-     make_unsigned_t<value_type> lowerVal = val[0] >> startBit; 
-     // Mask off upper bits after right shift in case of signed type. 
-     make_unsigned_t<value_type> numBitsFirstVal = 
-         (sizeof(value_type) * 8) - startBit; 
-     lowerVal &= ((make_unsigned_t<value_type>)1 << numBitsFirstVal) - 1; 
-   
-     // Get the bits from the upper value. 
-     make_unsigned_t<value_type> upperVal = 
-         val[1] & (((make_unsigned_t<value_type>)1 << startBit) - 1); 
-     // Shift them in to place. 
-     upperVal <<= numBitsFirstVal; 
-   
-     return lowerVal | upperVal; 
-   } 
- } 
-   
- /// Write a value to memory with a particular endianness, for a location 
- /// that starts at the given bit offset within the first byte. 
- template <typename value_type, endianness endian, std::size_t alignment> 
- inline void writeAtBitAlignment(void *memory, value_type value, 
-                                 uint64_t startBit) { 
-   assert(startBit < 8); 
-   if (startBit == 0) 
-     write<value_type, endian, alignment>(memory, value); 
-   else { 
-     // Read two values and shift the result into them. 
-     value_type val[2]; 
-     memcpy(&val[0], 
-            LLVM_ASSUME_ALIGNED( 
-                memory, (detail::PickAlignment<value_type, alignment>::value)), 
-            sizeof(value_type) * 2); 
-     val[0] = byte_swap<value_type, endian>(val[0]); 
-     val[1] = byte_swap<value_type, endian>(val[1]); 
-   
-     // Mask off any existing bits in the upper part of the lower value that 
-     // we want to replace. 
-     val[0] &= ((make_unsigned_t<value_type>)1 << startBit) - 1; 
-     make_unsigned_t<value_type> numBitsFirstVal = 
-         (sizeof(value_type) * 8) - startBit; 
-     make_unsigned_t<value_type> lowerVal = value; 
-     if (startBit > 0) { 
-       // Mask off the upper bits in the new value that are not going to go into 
-       // the lower value. This avoids a left shift of a negative value, which 
-       // is undefined behavior. 
-       lowerVal &= (((make_unsigned_t<value_type>)1 << numBitsFirstVal) - 1); 
-       // Now shift the new bits into place 
-       lowerVal <<= startBit; 
-     } 
-     val[0] |= lowerVal; 
-   
-     // Mask off any existing bits in the lower part of the upper value that 
-     // we want to replace. 
-     val[1] &= ~(((make_unsigned_t<value_type>)1 << startBit) - 1); 
-     // Next shift the bits that go into the upper value into position. 
-     make_unsigned_t<value_type> upperVal = value >> numBitsFirstVal; 
-     // Mask off upper bits after right shift in case of signed type. 
-     upperVal &= ((make_unsigned_t<value_type>)1 << startBit) - 1; 
-     val[1] |= upperVal; 
-   
-     // Finally, rewrite values. 
-     val[0] = byte_swap<value_type, endian>(val[0]); 
-     val[1] = byte_swap<value_type, endian>(val[1]); 
-     memcpy(LLVM_ASSUME_ALIGNED( 
-                memory, (detail::PickAlignment<value_type, alignment>::value)), 
-            &val[0], sizeof(value_type) * 2); 
-   } 
- } 
-   
- } // end namespace endian 
-   
- namespace detail { 
-   
- template <typename ValueType, endianness Endian, std::size_t Alignment, 
-           std::size_t ALIGN = PickAlignment<ValueType, Alignment>::value> 
- struct packed_endian_specific_integral { 
-   using value_type = ValueType; 
-   static constexpr endianness endian = Endian; 
-   static constexpr std::size_t alignment = Alignment; 
-   
-   packed_endian_specific_integral() = default; 
-   
-   explicit packed_endian_specific_integral(value_type val) { *this = val; } 
-   
-   operator value_type() const { 
-     return endian::read<value_type, endian, alignment>( 
-       (const void*)Value.buffer); 
-   } 
-   
-   void operator=(value_type newValue) { 
-     endian::write<value_type, endian, alignment>( 
-       (void*)Value.buffer, newValue); 
-   } 
-   
-   packed_endian_specific_integral &operator+=(value_type newValue) { 
-     *this = *this + newValue; 
-     return *this; 
-   } 
-   
-   packed_endian_specific_integral &operator-=(value_type newValue) { 
-     *this = *this - newValue; 
-     return *this; 
-   } 
-   
-   packed_endian_specific_integral &operator|=(value_type newValue) { 
-     *this = *this | newValue; 
-     return *this; 
-   } 
-   
-   packed_endian_specific_integral &operator&=(value_type newValue) { 
-     *this = *this & newValue; 
-     return *this; 
-   } 
-   
- private: 
-   struct { 
-     alignas(ALIGN) char buffer[sizeof(value_type)]; 
-   } Value; 
-   
- public: 
-   struct ref { 
-     explicit ref(void *Ptr) : Ptr(Ptr) {} 
-   
-     operator value_type() const { 
-       return endian::read<value_type, endian, alignment>(Ptr); 
-     } 
-   
-     void operator=(value_type NewValue) { 
-       endian::write<value_type, endian, alignment>(Ptr, NewValue); 
-     } 
-   
-   private: 
-     void *Ptr; 
-   }; 
- }; 
-   
- } // end namespace detail 
-   
- using ulittle16_t = 
-     detail::packed_endian_specific_integral<uint16_t, little, unaligned>; 
- using ulittle32_t = 
-     detail::packed_endian_specific_integral<uint32_t, little, unaligned>; 
- using ulittle64_t = 
-     detail::packed_endian_specific_integral<uint64_t, little, unaligned>; 
-   
- using little16_t = 
-     detail::packed_endian_specific_integral<int16_t, little, unaligned>; 
- using little32_t = 
-     detail::packed_endian_specific_integral<int32_t, little, unaligned>; 
- using little64_t = 
-     detail::packed_endian_specific_integral<int64_t, little, unaligned>; 
-   
- using aligned_ulittle16_t = 
-     detail::packed_endian_specific_integral<uint16_t, little, aligned>; 
- using aligned_ulittle32_t = 
-     detail::packed_endian_specific_integral<uint32_t, little, aligned>; 
- using aligned_ulittle64_t = 
-     detail::packed_endian_specific_integral<uint64_t, little, aligned>; 
-   
- using aligned_little16_t = 
-     detail::packed_endian_specific_integral<int16_t, little, aligned>; 
- using aligned_little32_t = 
-     detail::packed_endian_specific_integral<int32_t, little, aligned>; 
- using aligned_little64_t = 
-     detail::packed_endian_specific_integral<int64_t, little, aligned>; 
-   
- using ubig16_t = 
-     detail::packed_endian_specific_integral<uint16_t, big, unaligned>; 
- using ubig32_t = 
-     detail::packed_endian_specific_integral<uint32_t, big, unaligned>; 
- using ubig64_t = 
-     detail::packed_endian_specific_integral<uint64_t, big, unaligned>; 
-   
- using big16_t = 
-     detail::packed_endian_specific_integral<int16_t, big, unaligned>; 
- using big32_t = 
-     detail::packed_endian_specific_integral<int32_t, big, unaligned>; 
- using big64_t = 
-     detail::packed_endian_specific_integral<int64_t, big, unaligned>; 
-   
- using aligned_ubig16_t = 
-     detail::packed_endian_specific_integral<uint16_t, big, aligned>; 
- using aligned_ubig32_t = 
-     detail::packed_endian_specific_integral<uint32_t, big, aligned>; 
- using aligned_ubig64_t = 
-     detail::packed_endian_specific_integral<uint64_t, big, aligned>; 
-   
- using aligned_big16_t = 
-     detail::packed_endian_specific_integral<int16_t, big, aligned>; 
- using aligned_big32_t = 
-     detail::packed_endian_specific_integral<int32_t, big, aligned>; 
- using aligned_big64_t = 
-     detail::packed_endian_specific_integral<int64_t, big, aligned>; 
-   
- using unaligned_uint16_t = 
-     detail::packed_endian_specific_integral<uint16_t, native, unaligned>; 
- using unaligned_uint32_t = 
-     detail::packed_endian_specific_integral<uint32_t, native, unaligned>; 
- using unaligned_uint64_t = 
-     detail::packed_endian_specific_integral<uint64_t, native, unaligned>; 
-   
- using unaligned_int16_t = 
-     detail::packed_endian_specific_integral<int16_t, native, unaligned>; 
- using unaligned_int32_t = 
-     detail::packed_endian_specific_integral<int32_t, native, unaligned>; 
- using unaligned_int64_t = 
-     detail::packed_endian_specific_integral<int64_t, native, unaligned>; 
-   
- template <typename T> 
- using little_t = detail::packed_endian_specific_integral<T, little, unaligned>; 
- template <typename T> 
- using big_t = detail::packed_endian_specific_integral<T, big, unaligned>; 
-   
- template <typename T> 
- using aligned_little_t = 
-     detail::packed_endian_specific_integral<T, little, aligned>; 
- template <typename T> 
- using aligned_big_t = detail::packed_endian_specific_integral<T, big, aligned>; 
-   
- namespace endian { 
-   
- template <typename T> inline T read(const void *P, endianness E) { 
-   return read<T, unaligned>(P, E); 
- } 
-   
- template <typename T, endianness E> inline T read(const void *P) { 
-   return *(const detail::packed_endian_specific_integral<T, E, unaligned> *)P; 
- } 
-   
- inline uint16_t read16(const void *P, endianness E) { 
-   return read<uint16_t>(P, E); 
- } 
- inline uint32_t read32(const void *P, endianness E) { 
-   return read<uint32_t>(P, E); 
- } 
- inline uint64_t read64(const void *P, endianness E) { 
-   return read<uint64_t>(P, E); 
- } 
-   
- template <endianness E> inline uint16_t read16(const void *P) { 
-   return read<uint16_t, E>(P); 
- } 
- template <endianness E> inline uint32_t read32(const void *P) { 
-   return read<uint32_t, E>(P); 
- } 
- template <endianness E> inline uint64_t read64(const void *P) { 
-   return read<uint64_t, E>(P); 
- } 
-   
- inline uint16_t read16le(const void *P) { return read16<little>(P); } 
- inline uint32_t read32le(const void *P) { return read32<little>(P); } 
- inline uint64_t read64le(const void *P) { return read64<little>(P); } 
- inline uint16_t read16be(const void *P) { return read16<big>(P); } 
- inline uint32_t read32be(const void *P) { return read32<big>(P); } 
- inline uint64_t read64be(const void *P) { return read64<big>(P); } 
-   
- template <typename T> inline void write(void *P, T V, endianness E) { 
-   write<T, unaligned>(P, V, E); 
- } 
-   
- template <typename T, endianness E> inline void write(void *P, T V) { 
-   *(detail::packed_endian_specific_integral<T, E, unaligned> *)P = V; 
- } 
-   
- inline void write16(void *P, uint16_t V, endianness E) { 
-   write<uint16_t>(P, V, E); 
- } 
- inline void write32(void *P, uint32_t V, endianness E) { 
-   write<uint32_t>(P, V, E); 
- } 
- inline void write64(void *P, uint64_t V, endianness E) { 
-   write<uint64_t>(P, V, E); 
- } 
-   
- template <endianness E> inline void write16(void *P, uint16_t V) { 
-   write<uint16_t, E>(P, V); 
- } 
- template <endianness E> inline void write32(void *P, uint32_t V) { 
-   write<uint32_t, E>(P, V); 
- } 
- template <endianness E> inline void write64(void *P, uint64_t V) { 
-   write<uint64_t, E>(P, V); 
- } 
-   
- inline void write16le(void *P, uint16_t V) { write16<little>(P, V); } 
- inline void write32le(void *P, uint32_t V) { write32<little>(P, V); } 
- inline void write64le(void *P, uint64_t V) { write64<little>(P, V); } 
- inline void write16be(void *P, uint16_t V) { write16<big>(P, V); } 
- inline void write32be(void *P, uint32_t V) { write32<big>(P, V); } 
- inline void write64be(void *P, uint64_t V) { write64<big>(P, V); } 
-   
- } // end namespace endian 
-   
- } // end namespace support 
- } // end namespace llvm 
-   
- #endif // LLVM_SUPPORT_ENDIAN_H 
-