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