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