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