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