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