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/AlignOf.h" 17 #include "llvm/Support/Compiler.h" 18 #include "llvm/Support/Host.h" 19 #include "llvm/Support/SwapByteOrder.h" 20 #include <cassert> 21 #include <cstddef> 22 #include <cstdint> 23 #include <cstring> 24 #include <type_traits> 25 26 namespace llvm { 27 namespace support { 28 29 enum endianness {big, little, native}; 30 31 // These are named values for common alignments. 32 enum {aligned = 0, unaligned = 1}; 33 34 namespace detail { 35 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 }; 40 }; 41 42 } // end namespace detail 43 44 namespace endian { 45 46 constexpr endianness system_endianness() { 47 return sys::IsBigEndianHost ? big : little; 48 } 49 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); 54 return value; 55 } 56 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); 61 } 62 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) { 66 value_type ret; 67 68 memcpy(&ret, 69 LLVM_ASSUME_ALIGNED( 70 memory, (detail::PickAlignment<value_type, alignment>::value)), 71 sizeof(value_type)); 72 return byte_swap<value_type>(ret, endian); 73 } 74 75 template<typename value_type, 76 endianness endian, 77 std::size_t alignment> 78 inline value_type read(const void *memory) { 79 return read<value_type, alignment>(memory, endian); 80 } 81 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); 88 return ret; 89 } 90 91 template<typename value_type, endianness endian, std::size_t alignment, 92 typename CharT> 93 inline value_type readNext(const CharT *&memory) { 94 return readNext<value_type, alignment, CharT>(memory, endian); 95 } 96 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)); 104 } 105 106 template<typename value_type, 107 endianness endian, 108 std::size_t alignment> 109 inline void write(void *memory, value_type value) { 110 write<value_type, alignment>(memory, value, endian); 111 } 112 113 template <typename value_type> 114 using make_unsigned_t = typename std::make_unsigned<value_type>::type; 115 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); 121 if (startBit == 0) 122 return read<value_type, endian, alignment>(memory); 123 else { 124 // Read two values and compose the result from them. 125 value_type val[2]; 126 memcpy(&val[0], 127 LLVM_ASSUME_ALIGNED( 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]); 132 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; 139 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; 145 146 return lowerVal | upperVal; 147 } 148 } 149 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, 154 uint64_t startBit) { 155 assert(startBit < 8); 156 if (startBit == 0) 157 write<value_type, endian, alignment>(memory, value); 158 else { 159 // Read two values and shift the result into them. 160 value_type val[2]; 161 memcpy(&val[0], 162 LLVM_ASSUME_ALIGNED( 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]); 167 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; 174 if (startBit > 0) { 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; 181 } 182 val[0] |= lowerVal; 183 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; 191 val[1] |= upperVal; 192 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); 199 } 200 } 201 202 } // end namespace endian 203 204 namespace detail { 205 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; 212 213 packed_endian_specific_integral() = default; 214 215 explicit packed_endian_specific_integral(value_type val) { *this = val; } 216 217 operator value_type() const { 218 return endian::read<value_type, endian, alignment>( 219 (const void*)Value.buffer); 220 } 221 222 void operator=(value_type newValue) { 223 endian::write<value_type, endian, alignment>( 224 (void*)Value.buffer, newValue); 225 } 226 227 packed_endian_specific_integral &operator+=(value_type newValue) { 228 *this = *this + newValue; 229 return *this; 230 } 231 232 packed_endian_specific_integral &operator-=(value_type newValue) { 233 *this = *this - newValue; 234 return *this; 235 } 236 237 packed_endian_specific_integral &operator|=(value_type newValue) { 238 *this = *this | newValue; 239 return *this; 240 } 241 242 packed_endian_specific_integral &operator&=(value_type newValue) { 243 *this = *this & newValue; 244 return *this; 245 } 246 247 private: 248 struct { 249 alignas(ALIGN) char buffer[sizeof(value_type)]; 250 } Value; 251 252 public: 253 struct ref { 254 explicit ref(void *Ptr) : Ptr(Ptr) {} 255 256 operator value_type() const { 257 return endian::read<value_type, endian, alignment>(Ptr); 258 } 259 260 void operator=(value_type NewValue) { 261 endian::write<value_type, endian, alignment>(Ptr, NewValue); 262 } 263 264 private: 265 void *Ptr; 266 }; 267 }; 268 269 } // end namespace detail 270 271 using ulittle16_t = 272 detail::packed_endian_specific_integral<uint16_t, little, unaligned>; 273 using ulittle32_t = 274 detail::packed_endian_specific_integral<uint32_t, little, unaligned>; 275 using ulittle64_t = 276 detail::packed_endian_specific_integral<uint64_t, little, unaligned>; 277 278 using little16_t = 279 detail::packed_endian_specific_integral<int16_t, little, unaligned>; 280 using little32_t = 281 detail::packed_endian_specific_integral<int32_t, little, unaligned>; 282 using little64_t = 283 detail::packed_endian_specific_integral<int64_t, little, unaligned>; 284 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>; 291 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>; 298 299 using ubig16_t = 300 detail::packed_endian_specific_integral<uint16_t, big, unaligned>; 301 using ubig32_t = 302 detail::packed_endian_specific_integral<uint32_t, big, unaligned>; 303 using ubig64_t = 304 detail::packed_endian_specific_integral<uint64_t, big, unaligned>; 305 306 using big16_t = 307 detail::packed_endian_specific_integral<int16_t, big, unaligned>; 308 using big32_t = 309 detail::packed_endian_specific_integral<int32_t, big, unaligned>; 310 using big64_t = 311 detail::packed_endian_specific_integral<int64_t, big, unaligned>; 312 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>; 319 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>; 326 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>; 333 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>; 340 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>; 345 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>; 351 352 namespace endian { 353 354 template <typename T> inline T read(const void *P, endianness E) { 355 return read<T, unaligned>(P, E); 356 } 357 358 template <typename T, endianness E> inline T read(const void *P) { 359 return *(const detail::packed_endian_specific_integral<T, E, unaligned> *)P; 360 } 361 362 inline uint16_t read16(const void *P, endianness E) { 363 return read<uint16_t>(P, E); 364 } 365 inline uint32_t read32(const void *P, endianness E) { 366 return read<uint32_t>(P, E); 367 } 368 inline uint64_t read64(const void *P, endianness E) { 369 return read<uint64_t>(P, E); 370 } 371 372 template <endianness E> inline uint16_t read16(const void *P) { 373 return read<uint16_t, E>(P); 374 } 375 template <endianness E> inline uint32_t read32(const void *P) { 376 return read<uint32_t, E>(P); 377 } 378 template <endianness E> inline uint64_t read64(const void *P) { 379 return read<uint64_t, E>(P); 380 } 381 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); } 388 389 template <typename T> inline void write(void *P, T V, endianness E) { 390 write<T, unaligned>(P, V, E); 391 } 392 393 template <typename T, endianness E> inline void write(void *P, T V) { 394 *(detail::packed_endian_specific_integral<T, E, unaligned> *)P = V; 395 } 396 397 inline void write16(void *P, uint16_t V, endianness E) { 398 write<uint16_t>(P, V, E); 399 } 400 inline void write32(void *P, uint32_t V, endianness E) { 401 write<uint32_t>(P, V, E); 402 } 403 inline void write64(void *P, uint64_t V, endianness E) { 404 write<uint64_t>(P, V, E); 405 } 406 407 template <endianness E> inline void write16(void *P, uint16_t V) { 408 write<uint16_t, E>(P, V); 409 } 410 template <endianness E> inline void write32(void *P, uint32_t V) { 411 write<uint32_t, E>(P, V); 412 } 413 template <endianness E> inline void write64(void *P, uint64_t V) { 414 write<uint64_t, E>(P, V); 415 } 416 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); } 423 424 } // end namespace endian 425 426 } // end namespace support 427 } // end namespace llvm 428 429 #endif // LLVM_SUPPORT_ENDIAN_H 430