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