Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:44:29

0001 //===- Endian.h - Utilities for IO with endian specific data ----*- C++ -*-===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 //
0009 // This file declares generic functions to read and write endian specific data.
0010 //
0011 //===----------------------------------------------------------------------===//
0012 
0013 #ifndef LLVM_SUPPORT_ENDIAN_H
0014 #define LLVM_SUPPORT_ENDIAN_H
0015 
0016 #include "llvm/ADT/bit.h"
0017 #include "llvm/Support/Compiler.h"
0018 #include "llvm/Support/SwapByteOrder.h"
0019 #include <cassert>
0020 #include <cstddef>
0021 #include <cstdint>
0022 #include <cstring>
0023 #include <type_traits>
0024 
0025 namespace llvm {
0026 namespace support {
0027 
0028 // These are named values for common alignments.
0029 enum {aligned = 0, unaligned = 1};
0030 
0031 namespace detail {
0032 
0033 /// ::value is either alignment, or alignof(T) if alignment is 0.
0034 template<class T, int alignment>
0035 struct PickAlignment {
0036  enum { value = alignment == 0 ? alignof(T) : alignment };
0037 };
0038 
0039 } // end namespace detail
0040 
0041 namespace endian {
0042 
0043 template <typename value_type>
0044 [[nodiscard]] inline value_type byte_swap(value_type value, endianness endian) {
0045   if (endian != llvm::endianness::native)
0046     sys::swapByteOrder(value);
0047   return value;
0048 }
0049 
0050 /// Swap the bytes of value to match the given endianness.
0051 template <typename value_type, endianness endian>
0052 [[nodiscard]] inline value_type byte_swap(value_type value) {
0053   return byte_swap(value, endian);
0054 }
0055 
0056 /// Read a value of a particular endianness from memory.
0057 template <typename value_type, std::size_t alignment = unaligned>
0058 [[nodiscard]] inline value_type read(const void *memory, endianness endian) {
0059   value_type ret;
0060 
0061   memcpy(static_cast<void *>(&ret),
0062          LLVM_ASSUME_ALIGNED(
0063              memory, (detail::PickAlignment<value_type, alignment>::value)),
0064          sizeof(value_type));
0065   return byte_swap<value_type>(ret, endian);
0066 }
0067 
0068 template <typename value_type, endianness endian, std::size_t alignment>
0069 [[nodiscard]] inline value_type read(const void *memory) {
0070   return read<value_type, alignment>(memory, endian);
0071 }
0072 
0073 /// Read a value of a particular endianness from a buffer, and increment the
0074 /// buffer past that value.
0075 template <typename value_type, std::size_t alignment = unaligned,
0076           typename CharT>
0077 [[nodiscard]] inline value_type readNext(const CharT *&memory,
0078                                          endianness endian) {
0079   value_type ret = read<value_type, alignment>(memory, endian);
0080   memory += sizeof(value_type);
0081   return ret;
0082 }
0083 
0084 template <typename value_type, endianness endian,
0085           std::size_t alignment = unaligned, typename CharT>
0086 [[nodiscard]] inline value_type readNext(const CharT *&memory) {
0087   return readNext<value_type, alignment, CharT>(memory, endian);
0088 }
0089 
0090 /// Write a value to memory with a particular endianness.
0091 template <typename value_type, std::size_t alignment = unaligned>
0092 inline void write(void *memory, value_type value, endianness endian) {
0093   value = byte_swap<value_type>(value, endian);
0094   memcpy(LLVM_ASSUME_ALIGNED(
0095              memory, (detail::PickAlignment<value_type, alignment>::value)),
0096          &value, sizeof(value_type));
0097 }
0098 
0099 template<typename value_type,
0100          endianness endian,
0101          std::size_t alignment>
0102 inline void write(void *memory, value_type value) {
0103   write<value_type, alignment>(memory, value, endian);
0104 }
0105 
0106 /// Write a value of a particular endianness, and increment the buffer past that
0107 /// value.
0108 template <typename value_type, std::size_t alignment = unaligned,
0109           typename CharT>
0110 inline void writeNext(CharT *&memory, value_type value, endianness endian) {
0111   write(memory, value, endian);
0112   memory += sizeof(value_type);
0113 }
0114 
0115 template <typename value_type, endianness endian,
0116           std::size_t alignment = unaligned, typename CharT>
0117 inline void writeNext(CharT *&memory, value_type value) {
0118   writeNext<value_type, alignment, CharT>(memory, value, endian);
0119 }
0120 
0121 template <typename value_type>
0122 using make_unsigned_t = std::make_unsigned_t<value_type>;
0123 
0124 /// Read a value of a particular endianness from memory, for a location
0125 /// that starts at the given bit offset within the first byte.
0126 template <typename value_type, endianness endian, std::size_t alignment>
0127 [[nodiscard]] inline value_type readAtBitAlignment(const void *memory,
0128                                                    uint64_t startBit) {
0129   assert(startBit < 8);
0130   if (startBit == 0)
0131     return read<value_type, endian, alignment>(memory);
0132   else {
0133     // Read two values and compose the result from them.
0134     value_type val[2];
0135     memcpy(&val[0],
0136            LLVM_ASSUME_ALIGNED(
0137                memory, (detail::PickAlignment<value_type, alignment>::value)),
0138            sizeof(value_type) * 2);
0139     val[0] = byte_swap<value_type, endian>(val[0]);
0140     val[1] = byte_swap<value_type, endian>(val[1]);
0141 
0142     // Shift bits from the lower value into place.
0143     make_unsigned_t<value_type> lowerVal = val[0] >> startBit;
0144     // Mask off upper bits after right shift in case of signed type.
0145     make_unsigned_t<value_type> numBitsFirstVal =
0146         (sizeof(value_type) * 8) - startBit;
0147     lowerVal &= ((make_unsigned_t<value_type>)1 << numBitsFirstVal) - 1;
0148 
0149     // Get the bits from the upper value.
0150     make_unsigned_t<value_type> upperVal =
0151         val[1] & (((make_unsigned_t<value_type>)1 << startBit) - 1);
0152     // Shift them in to place.
0153     upperVal <<= numBitsFirstVal;
0154 
0155     return lowerVal | upperVal;
0156   }
0157 }
0158 
0159 /// Write a value to memory with a particular endianness, for a location
0160 /// that starts at the given bit offset within the first byte.
0161 template <typename value_type, endianness endian, std::size_t alignment>
0162 inline void writeAtBitAlignment(void *memory, value_type value,
0163                                 uint64_t startBit) {
0164   assert(startBit < 8);
0165   if (startBit == 0)
0166     write<value_type, endian, alignment>(memory, value);
0167   else {
0168     // Read two values and shift the result into them.
0169     value_type val[2];
0170     memcpy(&val[0],
0171            LLVM_ASSUME_ALIGNED(
0172                memory, (detail::PickAlignment<value_type, alignment>::value)),
0173            sizeof(value_type) * 2);
0174     val[0] = byte_swap<value_type, endian>(val[0]);
0175     val[1] = byte_swap<value_type, endian>(val[1]);
0176 
0177     // Mask off any existing bits in the upper part of the lower value that
0178     // we want to replace.
0179     val[0] &= ((make_unsigned_t<value_type>)1 << startBit) - 1;
0180     make_unsigned_t<value_type> numBitsFirstVal =
0181         (sizeof(value_type) * 8) - startBit;
0182     make_unsigned_t<value_type> lowerVal = value;
0183     if (startBit > 0) {
0184       // Mask off the upper bits in the new value that are not going to go into
0185       // the lower value. This avoids a left shift of a negative value, which
0186       // is undefined behavior.
0187       lowerVal &= (((make_unsigned_t<value_type>)1 << numBitsFirstVal) - 1);
0188       // Now shift the new bits into place
0189       lowerVal <<= startBit;
0190     }
0191     val[0] |= lowerVal;
0192 
0193     // Mask off any existing bits in the lower part of the upper value that
0194     // we want to replace.
0195     val[1] &= ~(((make_unsigned_t<value_type>)1 << startBit) - 1);
0196     // Next shift the bits that go into the upper value into position.
0197     make_unsigned_t<value_type> upperVal = value >> numBitsFirstVal;
0198     // Mask off upper bits after right shift in case of signed type.
0199     upperVal &= ((make_unsigned_t<value_type>)1 << startBit) - 1;
0200     val[1] |= upperVal;
0201 
0202     // Finally, rewrite values.
0203     val[0] = byte_swap<value_type, endian>(val[0]);
0204     val[1] = byte_swap<value_type, endian>(val[1]);
0205     memcpy(LLVM_ASSUME_ALIGNED(
0206                memory, (detail::PickAlignment<value_type, alignment>::value)),
0207            &val[0], sizeof(value_type) * 2);
0208   }
0209 }
0210 
0211 } // end namespace endian
0212 
0213 namespace detail {
0214 
0215 template <typename ValueType, endianness Endian, std::size_t Alignment,
0216           std::size_t ALIGN = PickAlignment<ValueType, Alignment>::value>
0217 struct packed_endian_specific_integral {
0218   using value_type = ValueType;
0219   static constexpr endianness endian = Endian;
0220   static constexpr std::size_t alignment = Alignment;
0221 
0222   packed_endian_specific_integral() = default;
0223 
0224   explicit packed_endian_specific_integral(value_type val) { *this = val; }
0225 
0226   operator value_type() const {
0227     return endian::read<value_type, endian, alignment>(
0228       (const void*)Value.buffer);
0229   }
0230 
0231   void operator=(value_type newValue) {
0232     endian::write<value_type, endian, alignment>(
0233       (void*)Value.buffer, newValue);
0234   }
0235 
0236   packed_endian_specific_integral &operator+=(value_type newValue) {
0237     *this = *this + newValue;
0238     return *this;
0239   }
0240 
0241   packed_endian_specific_integral &operator-=(value_type newValue) {
0242     *this = *this - newValue;
0243     return *this;
0244   }
0245 
0246   packed_endian_specific_integral &operator|=(value_type newValue) {
0247     *this = *this | newValue;
0248     return *this;
0249   }
0250 
0251   packed_endian_specific_integral &operator&=(value_type newValue) {
0252     *this = *this & newValue;
0253     return *this;
0254   }
0255 
0256 private:
0257   struct {
0258     alignas(ALIGN) char buffer[sizeof(value_type)];
0259   } Value;
0260 
0261 public:
0262   struct ref {
0263     explicit ref(void *Ptr) : Ptr(Ptr) {}
0264 
0265     operator value_type() const {
0266       return endian::read<value_type, endian, alignment>(Ptr);
0267     }
0268 
0269     void operator=(value_type NewValue) {
0270       endian::write<value_type, endian, alignment>(Ptr, NewValue);
0271     }
0272 
0273   private:
0274     void *Ptr;
0275   };
0276 };
0277 
0278 } // end namespace detail
0279 
0280 using ulittle16_t =
0281     detail::packed_endian_specific_integral<uint16_t, llvm::endianness::little,
0282                                             unaligned>;
0283 using ulittle32_t =
0284     detail::packed_endian_specific_integral<uint32_t, llvm::endianness::little,
0285                                             unaligned>;
0286 using ulittle64_t =
0287     detail::packed_endian_specific_integral<uint64_t, llvm::endianness::little,
0288                                             unaligned>;
0289 
0290 using little16_t =
0291     detail::packed_endian_specific_integral<int16_t, llvm::endianness::little,
0292                                             unaligned>;
0293 using little32_t =
0294     detail::packed_endian_specific_integral<int32_t, llvm::endianness::little,
0295                                             unaligned>;
0296 using little64_t =
0297     detail::packed_endian_specific_integral<int64_t, llvm::endianness::little,
0298                                             unaligned>;
0299 
0300 using aligned_ulittle16_t =
0301     detail::packed_endian_specific_integral<uint16_t, llvm::endianness::little,
0302                                             aligned>;
0303 using aligned_ulittle32_t =
0304     detail::packed_endian_specific_integral<uint32_t, llvm::endianness::little,
0305                                             aligned>;
0306 using aligned_ulittle64_t =
0307     detail::packed_endian_specific_integral<uint64_t, llvm::endianness::little,
0308                                             aligned>;
0309 
0310 using aligned_little16_t =
0311     detail::packed_endian_specific_integral<int16_t, llvm::endianness::little,
0312                                             aligned>;
0313 using aligned_little32_t =
0314     detail::packed_endian_specific_integral<int32_t, llvm::endianness::little,
0315                                             aligned>;
0316 using aligned_little64_t =
0317     detail::packed_endian_specific_integral<int64_t, llvm::endianness::little,
0318                                             aligned>;
0319 
0320 using ubig16_t =
0321     detail::packed_endian_specific_integral<uint16_t, llvm::endianness::big,
0322                                             unaligned>;
0323 using ubig32_t =
0324     detail::packed_endian_specific_integral<uint32_t, llvm::endianness::big,
0325                                             unaligned>;
0326 using ubig64_t =
0327     detail::packed_endian_specific_integral<uint64_t, llvm::endianness::big,
0328                                             unaligned>;
0329 
0330 using big16_t =
0331     detail::packed_endian_specific_integral<int16_t, llvm::endianness::big,
0332                                             unaligned>;
0333 using big32_t =
0334     detail::packed_endian_specific_integral<int32_t, llvm::endianness::big,
0335                                             unaligned>;
0336 using big64_t =
0337     detail::packed_endian_specific_integral<int64_t, llvm::endianness::big,
0338                                             unaligned>;
0339 
0340 using aligned_ubig16_t =
0341     detail::packed_endian_specific_integral<uint16_t, llvm::endianness::big,
0342                                             aligned>;
0343 using aligned_ubig32_t =
0344     detail::packed_endian_specific_integral<uint32_t, llvm::endianness::big,
0345                                             aligned>;
0346 using aligned_ubig64_t =
0347     detail::packed_endian_specific_integral<uint64_t, llvm::endianness::big,
0348                                             aligned>;
0349 
0350 using aligned_big16_t =
0351     detail::packed_endian_specific_integral<int16_t, llvm::endianness::big,
0352                                             aligned>;
0353 using aligned_big32_t =
0354     detail::packed_endian_specific_integral<int32_t, llvm::endianness::big,
0355                                             aligned>;
0356 using aligned_big64_t =
0357     detail::packed_endian_specific_integral<int64_t, llvm::endianness::big,
0358                                             aligned>;
0359 
0360 using unaligned_uint16_t =
0361     detail::packed_endian_specific_integral<uint16_t, llvm::endianness::native,
0362                                             unaligned>;
0363 using unaligned_uint32_t =
0364     detail::packed_endian_specific_integral<uint32_t, llvm::endianness::native,
0365                                             unaligned>;
0366 using unaligned_uint64_t =
0367     detail::packed_endian_specific_integral<uint64_t, llvm::endianness::native,
0368                                             unaligned>;
0369 
0370 using unaligned_int16_t =
0371     detail::packed_endian_specific_integral<int16_t, llvm::endianness::native,
0372                                             unaligned>;
0373 using unaligned_int32_t =
0374     detail::packed_endian_specific_integral<int32_t, llvm::endianness::native,
0375                                             unaligned>;
0376 using unaligned_int64_t =
0377     detail::packed_endian_specific_integral<int64_t, llvm::endianness::native,
0378                                             unaligned>;
0379 
0380 template <typename T>
0381 using little_t =
0382     detail::packed_endian_specific_integral<T, llvm::endianness::little,
0383                                             unaligned>;
0384 template <typename T>
0385 using big_t = detail::packed_endian_specific_integral<T, llvm::endianness::big,
0386                                                       unaligned>;
0387 
0388 template <typename T>
0389 using aligned_little_t =
0390     detail::packed_endian_specific_integral<T, llvm::endianness::little,
0391                                             aligned>;
0392 template <typename T>
0393 using aligned_big_t =
0394     detail::packed_endian_specific_integral<T, llvm::endianness::big, aligned>;
0395 
0396 namespace endian {
0397 
0398 template <typename T, endianness E> [[nodiscard]] inline T read(const void *P) {
0399   return *(const detail::packed_endian_specific_integral<T, E, unaligned> *)P;
0400 }
0401 
0402 [[nodiscard]] inline uint16_t read16(const void *P, endianness E) {
0403   return read<uint16_t>(P, E);
0404 }
0405 [[nodiscard]] inline uint32_t read32(const void *P, endianness E) {
0406   return read<uint32_t>(P, E);
0407 }
0408 [[nodiscard]] inline uint64_t read64(const void *P, endianness E) {
0409   return read<uint64_t>(P, E);
0410 }
0411 
0412 template <endianness E> [[nodiscard]] inline uint16_t read16(const void *P) {
0413   return read<uint16_t, E>(P);
0414 }
0415 template <endianness E> [[nodiscard]] inline uint32_t read32(const void *P) {
0416   return read<uint32_t, E>(P);
0417 }
0418 template <endianness E> [[nodiscard]] inline uint64_t read64(const void *P) {
0419   return read<uint64_t, E>(P);
0420 }
0421 
0422 [[nodiscard]] inline uint16_t read16le(const void *P) {
0423   return read16<llvm::endianness::little>(P);
0424 }
0425 [[nodiscard]] inline uint32_t read32le(const void *P) {
0426   return read32<llvm::endianness::little>(P);
0427 }
0428 [[nodiscard]] inline uint64_t read64le(const void *P) {
0429   return read64<llvm::endianness::little>(P);
0430 }
0431 [[nodiscard]] inline uint16_t read16be(const void *P) {
0432   return read16<llvm::endianness::big>(P);
0433 }
0434 [[nodiscard]] inline uint32_t read32be(const void *P) {
0435   return read32<llvm::endianness::big>(P);
0436 }
0437 [[nodiscard]] inline uint64_t read64be(const void *P) {
0438   return read64<llvm::endianness::big>(P);
0439 }
0440 
0441 template <typename T, endianness E> inline void write(void *P, T V) {
0442   *(detail::packed_endian_specific_integral<T, E, unaligned> *)P = V;
0443 }
0444 
0445 inline void write16(void *P, uint16_t V, endianness E) {
0446   write<uint16_t>(P, V, E);
0447 }
0448 inline void write32(void *P, uint32_t V, endianness E) {
0449   write<uint32_t>(P, V, E);
0450 }
0451 inline void write64(void *P, uint64_t V, endianness E) {
0452   write<uint64_t>(P, V, E);
0453 }
0454 
0455 template <endianness E> inline void write16(void *P, uint16_t V) {
0456   write<uint16_t, E>(P, V);
0457 }
0458 template <endianness E> inline void write32(void *P, uint32_t V) {
0459   write<uint32_t, E>(P, V);
0460 }
0461 template <endianness E> inline void write64(void *P, uint64_t V) {
0462   write<uint64_t, E>(P, V);
0463 }
0464 
0465 inline void write16le(void *P, uint16_t V) {
0466   write16<llvm::endianness::little>(P, V);
0467 }
0468 inline void write32le(void *P, uint32_t V) {
0469   write32<llvm::endianness::little>(P, V);
0470 }
0471 inline void write64le(void *P, uint64_t V) {
0472   write64<llvm::endianness::little>(P, V);
0473 }
0474 inline void write16be(void *P, uint16_t V) {
0475   write16<llvm::endianness::big>(P, V);
0476 }
0477 inline void write32be(void *P, uint32_t V) {
0478   write32<llvm::endianness::big>(P, V);
0479 }
0480 inline void write64be(void *P, uint64_t V) {
0481   write64<llvm::endianness::big>(P, V);
0482 }
0483 
0484 } // end namespace endian
0485 
0486 } // end namespace support
0487 } // end namespace llvm
0488 
0489 #endif // LLVM_SUPPORT_ENDIAN_H