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 26namespace llvm { 27namespace support { 28 29enum endianness {big, little, native}; 30 31// These are named values for common alignments. 32enum {aligned = 0, unaligned = 1}; 33 34namespace detail { 35 36/// ::value is either alignment, or alignof(T) if alignment is 0. 37template<class T, int alignment> 38struct PickAlignment { 39 enum { value = alignment == 0 ? alignof(T) : alignment }; 40}; 41 42} // end namespace detail 43 44namespace endian { 45 46constexpr endianness system_endianness() { 47 return sys::IsBigEndianHost ? big : little; 48} 49 50template <typename value_type> 51inline 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. 58template<typename value_type, endianness endian> 59inline 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. 64template <typename value_type, std::size_t alignment> 65inline 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 75template<typename value_type, 76 endianness endian, 77 std::size_t alignment> 78inline 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. 84template <typename value_type, std::size_t alignment, typename CharT> 85inline 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 91template<typename value_type, endianness endian, std::size_t alignment, 92 typename CharT> 93inline 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. 98template <typename value_type, std::size_t alignment> 99inline 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 106template<typename value_type, 107 endianness endian, 108 std::size_t alignment> 109inline void write(void *memory, value_type value) { 110 write<value_type, alignment>(memory, value, endian); 111} 112 113template <typename value_type> 114using 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. 118template <typename value_type, endianness endian, std::size_t alignment> 119inline 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. 152template <typename value_type, endianness endian, std::size_t alignment> 153inline 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 204namespace detail { 205 206template <typename ValueType, endianness Endian, std::size_t Alignment, 207 std::size_t ALIGN = PickAlignment<ValueType, Alignment>::value> 208struct 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 247private: 248 struct { 249 alignas(ALIGN) char buffer[sizeof(value_type)]; 250 } Value; 251 252public: 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 271using ulittle16_t = 272 detail::packed_endian_specific_integral<uint16_t, little, unaligned>; 273using ulittle32_t = 274 detail::packed_endian_specific_integral<uint32_t, little, unaligned>; 275using ulittle64_t = 276 detail::packed_endian_specific_integral<uint64_t, little, unaligned>; 277 278using little16_t = 279 detail::packed_endian_specific_integral<int16_t, little, unaligned>; 280using little32_t = 281 detail::packed_endian_specific_integral<int32_t, little, unaligned>; 282using little64_t = 283 detail::packed_endian_specific_integral<int64_t, little, unaligned>; 284 285using aligned_ulittle16_t = 286 detail::packed_endian_specific_integral<uint16_t, little, aligned>; 287using aligned_ulittle32_t = 288 detail::packed_endian_specific_integral<uint32_t, little, aligned>; 289using aligned_ulittle64_t = 290 detail::packed_endian_specific_integral<uint64_t, little, aligned>; 291 292using aligned_little16_t = 293 detail::packed_endian_specific_integral<int16_t, little, aligned>; 294using aligned_little32_t = 295 detail::packed_endian_specific_integral<int32_t, little, aligned>; 296using aligned_little64_t = 297 detail::packed_endian_specific_integral<int64_t, little, aligned>; 298 299using ubig16_t = 300 detail::packed_endian_specific_integral<uint16_t, big, unaligned>; 301using ubig32_t = 302 detail::packed_endian_specific_integral<uint32_t, big, unaligned>; 303using ubig64_t = 304 detail::packed_endian_specific_integral<uint64_t, big, unaligned>; 305 306using big16_t = 307 detail::packed_endian_specific_integral<int16_t, big, unaligned>; 308using big32_t = 309 detail::packed_endian_specific_integral<int32_t, big, unaligned>; 310using big64_t = 311 detail::packed_endian_specific_integral<int64_t, big, unaligned>; 312 313using aligned_ubig16_t = 314 detail::packed_endian_specific_integral<uint16_t, big, aligned>; 315using aligned_ubig32_t = 316 detail::packed_endian_specific_integral<uint32_t, big, aligned>; 317using aligned_ubig64_t = 318 detail::packed_endian_specific_integral<uint64_t, big, aligned>; 319 320using aligned_big16_t = 321 detail::packed_endian_specific_integral<int16_t, big, aligned>; 322using aligned_big32_t = 323 detail::packed_endian_specific_integral<int32_t, big, aligned>; 324using aligned_big64_t = 325 detail::packed_endian_specific_integral<int64_t, big, aligned>; 326 327using unaligned_uint16_t = 328 detail::packed_endian_specific_integral<uint16_t, native, unaligned>; 329using unaligned_uint32_t = 330 detail::packed_endian_specific_integral<uint32_t, native, unaligned>; 331using unaligned_uint64_t = 332 detail::packed_endian_specific_integral<uint64_t, native, unaligned>; 333 334using unaligned_int16_t = 335 detail::packed_endian_specific_integral<int16_t, native, unaligned>; 336using unaligned_int32_t = 337 detail::packed_endian_specific_integral<int32_t, native, unaligned>; 338using unaligned_int64_t = 339 detail::packed_endian_specific_integral<int64_t, native, unaligned>; 340 341template <typename T> 342using little_t = detail::packed_endian_specific_integral<T, little, unaligned>; 343template <typename T> 344using big_t = detail::packed_endian_specific_integral<T, big, unaligned>; 345 346template <typename T> 347using aligned_little_t = 348 detail::packed_endian_specific_integral<T, little, aligned>; 349template <typename T> 350using aligned_big_t = detail::packed_endian_specific_integral<T, big, aligned>; 351 352namespace endian { 353 354template <typename T> inline T read(const void *P, endianness E) { 355 return read<T, unaligned>(P, E); 356} 357 358template <typename T, endianness E> inline T read(const void *P) { 359 return *(const detail::packed_endian_specific_integral<T, E, unaligned> *)P; 360} 361 362inline uint16_t read16(const void *P, endianness E) { 363 return read<uint16_t>(P, E); 364} 365inline uint32_t read32(const void *P, endianness E) { 366 return read<uint32_t>(P, E); 367} 368inline uint64_t read64(const void *P, endianness E) { 369 return read<uint64_t>(P, E); 370} 371 372template <endianness E> inline uint16_t read16(const void *P) { 373 return read<uint16_t, E>(P); 374} 375template <endianness E> inline uint32_t read32(const void *P) { 376 return read<uint32_t, E>(P); 377} 378template <endianness E> inline uint64_t read64(const void *P) { 379 return read<uint64_t, E>(P); 380} 381 382inline uint16_t read16le(const void *P) { return read16<little>(P); } 383inline uint32_t read32le(const void *P) { return read32<little>(P); } 384inline uint64_t read64le(const void *P) { return read64<little>(P); } 385inline uint16_t read16be(const void *P) { return read16<big>(P); } 386inline uint32_t read32be(const void *P) { return read32<big>(P); } 387inline uint64_t read64be(const void *P) { return read64<big>(P); } 388 389template <typename T> inline void write(void *P, T V, endianness E) { 390 write<T, unaligned>(P, V, E); 391} 392 393template <typename T, endianness E> inline void write(void *P, T V) { 394 *(detail::packed_endian_specific_integral<T, E, unaligned> *)P = V; 395} 396 397inline void write16(void *P, uint16_t V, endianness E) { 398 write<uint16_t>(P, V, E); 399} 400inline void write32(void *P, uint32_t V, endianness E) { 401 write<uint32_t>(P, V, E); 402} 403inline void write64(void *P, uint64_t V, endianness E) { 404 write<uint64_t>(P, V, E); 405} 406 407template <endianness E> inline void write16(void *P, uint16_t V) { 408 write<uint16_t, E>(P, V); 409} 410template <endianness E> inline void write32(void *P, uint32_t V) { 411 write<uint32_t, E>(P, V); 412} 413template <endianness E> inline void write64(void *P, uint64_t V) { 414 write<uint64_t, E>(P, V); 415} 416 417inline void write16le(void *P, uint16_t V) { write16<little>(P, V); } 418inline void write32le(void *P, uint32_t V) { write32<little>(P, V); } 419inline void write64le(void *P, uint64_t V) { write64<little>(P, V); } 420inline void write16be(void *P, uint16_t V) { write16<big>(P, V); } 421inline void write32be(void *P, uint32_t V) { write32<big>(P, V); } 422inline 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