// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #ifndef RUNTIME_PLATFORM_UTILS_H_ #define RUNTIME_PLATFORM_UTILS_H_ #include #include #include #include #include #include "platform/assert.h" #include "platform/globals.h" #include "platform/undefined_behavior_sanitizer.h" namespace dart { template class CAllocUniquePtr : public std::unique_ptr { public: CAllocUniquePtr() : std::unique_ptr(nullptr, std::free) {} explicit CAllocUniquePtr(T* value) : std::unique_ptr(value, std::free) {} CAllocUniquePtr& operator=(std::nullptr_t value) { std::unique_ptr::operator=(value); return *this; } }; using CStringUniquePtr = CAllocUniquePtr; class Utils { public: template static inline T Minimum(T x, T y) { return x < y ? x : y; } template static constexpr inline T Maximum(T x, T y) { return x > y ? x : y; } // Calculates absolute value of a given signed integer. // `x` must not be equal to minimum value representable by `T` // as its absolute value is out of range. template static inline T Abs(T x) { // Note: as a general rule, it is not OK to use STL in Dart VM. // However, std::numeric_limits::min() and max() are harmless // and worthwhile exception from this rule. ASSERT(x != std::numeric_limits::min()); if (x < 0) return -x; return x; } // Calculates absolute value of a given signed integer with saturation. // If `x` equals to minimum value representable by `T`, then // absolute value is saturated to the maximum value representable by `T`. template static inline T AbsWithSaturation(T x) { if (x < 0) { // Note: as a general rule, it is not OK to use STL in Dart VM. // However, std::numeric_limits::min() and max() are harmless // and worthwhile exception from this rule. if (x == std::numeric_limits::min()) { return std::numeric_limits::max(); } return -x; } return x; } template static constexpr bool IsPowerOfTwo(T x) { using Unsigned = typename std::make_unsigned::type; return std::has_single_bit(static_cast(x)); } template static constexpr int ShiftForPowerOfTwo(T x) { ASSERT(IsPowerOfTwo(x)); int num_shifts = 0; while (x > 1) { num_shifts++; x = x >> 1; } return num_shifts; } template static constexpr bool IsAligned(T x, uintptr_t alignment, uintptr_t offset = 0) { ASSERT(IsPowerOfTwo(alignment)); ASSERT(offset < alignment); return (x & (alignment - 1)) == offset; } template static constexpr bool IsAligned(T* x, uintptr_t alignment, uintptr_t offset = 0) { return IsAligned(reinterpret_cast(x), alignment, offset); } template static constexpr inline T RoundDown(T x, intptr_t alignment) { ASSERT(IsPowerOfTwo(alignment)); return (x & -alignment); } template static inline T* RoundDown(T* x, intptr_t alignment) { return reinterpret_cast( RoundDown(reinterpret_cast(x), alignment)); } template static constexpr inline T RoundUp(T x, uintptr_t alignment, uintptr_t offset = 0) { ASSERT(offset < alignment); return RoundDown(x + alignment - 1 + offset, alignment) - offset; } template static inline T* RoundUp(T* x, uintptr_t alignment, uintptr_t offset = 0) { return reinterpret_cast( RoundUp(reinterpret_cast(x), alignment, offset)); } static constexpr uintptr_t RoundUpToPowerOfTwo(uintptr_t x) { if (x == 0) return 0; return std::bit_ceil(x); } static constexpr int CountOneBits64(uint64_t x) { return std::popcount(x); } static constexpr int CountOneBits32(uint32_t x) { return std::popcount(x); } static constexpr int CountOneBitsWord(uword x) { return std::popcount(x); } // TODO(koda): Compare to flsll call/intrinsic. static constexpr size_t HighestBit(int64_t v) { uint64_t x = static_cast((v > 0) ? v : -v); uint64_t t = 0; size_t r = 0; if ((t = x >> 32) != 0) { x = t; r += 32; } if ((t = x >> 16) != 0) { x = t; r += 16; } if ((t = x >> 8) != 0) { x = t; r += 8; } if ((t = x >> 4) != 0) { x = t; r += 4; } if ((t = x >> 2) != 0) { x = t; r += 2; } if (x > 1) r += 1; return r; } static constexpr size_t BitLength(int64_t value) { // Flip bits if negative (-1 becomes 0). value ^= value >> (8 * sizeof(value) - 1); return std::bit_width(static_cast(value)); } static int CountLeadingZeros32(uint32_t x) { return std::countl_zero(x); } static int CountLeadingZeros64(uint64_t x) { return std::countl_zero(x); } static int CountLeadingZerosWord(uword x) { return std::countl_zero(x); } static int CountTrailingZeros32(uint32_t x) { return std::countr_zero(x); } static int CountTrailingZeros64(uint64_t x) { return std::countr_zero(x); } static int CountTrailingZerosWord(uword x) { return std::countr_zero(x); } static uint64_t ReverseBits64(uint64_t x); static uint32_t ReverseBits32(uint32_t x); static uword ReverseBitsWord(uword x) { #ifdef ARCH_IS_64_BIT return ReverseBits64(x); #else return ReverseBits32(x); #endif } // Computes magic numbers to implement DIV or MOD operator. static void CalculateMagicAndShiftForDivRem(int64_t divisor, int64_t* magic, int64_t* shift); // Computes a hash value for the given series of bytes. static uint32_t StringHash(const void* data, int length); // Computes a hash value for the given word. static uint32_t WordHash(intptr_t key); // Check whether an N-bit two's-complement representation can hold value. template static inline bool IsInt(intptr_t N, T value) { ASSERT(N >= 1); constexpr intptr_t value_size_in_bits = kBitsPerByte * sizeof(T); if constexpr (std::is_signed::value) { if (N >= value_size_in_bits) return true; // Trivially fits. const T limit = static_cast(1) << (N - 1); return (-limit <= value) && (value < limit); } else { if (N > value_size_in_bits) return true; // Trivially fits. const T limit = static_cast(1) << (N - 1); return value < limit; } } template static inline bool IsUint(intptr_t N, T value) { ASSERT(N >= 1); constexpr intptr_t value_size_in_bits = kBitsPerByte * sizeof(T); if constexpr (std::is_signed::value) { if (value < 0) return false; // Not an unsigned value. if (N >= value_size_in_bits - 1) { return true; // N can fit the magnitude bits. } } else { if (N >= value_size_in_bits) return true; // Trivially fits. } const T limit = (static_cast(1) << N) - 1; return value <= limit; } // Check whether the magnitude of value fits in N bits. This differs from // IsInt(N + 1, value) only in that this returns false for the minimum value // of a N+1 bit two's complement value. // // Primarily used for testing whether a two's complement value can be used in // a place where the sign is replaced with a marker that says whether the // magnitude is added or subtracted, e.g., the U bit (bit 23) in some ARM7 // instructions. template static inline bool MagnitudeIsUint(intptr_t N, T value) { ASSERT(N >= 1); if constexpr (std::is_signed::value) { using Unsigned = typename std::make_unsigned::type; if (value < 0) return IsUint(N, -value); } return IsUint(N, value); } static inline int32_t Low16Bits(int32_t value) { return static_cast(value & 0xffff); } static inline int32_t High16Bits(int32_t value) { return static_cast(value >> 16); } static inline int32_t Low32Bits(int64_t value) { return static_cast(value); } static inline int32_t High32Bits(int64_t value) { return static_cast(value >> 32); } static inline int64_t LowHighTo64Bits(uint32_t low, int32_t high) { return (static_cast(high) << 32) | (low & 0x0ffffffffLL); } static inline constexpr bool IsAlphaNumeric(uint32_t c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || IsDecimalDigit(c); } static inline constexpr bool IsDecimalDigit(uint32_t c) { return ('0' <= c) && (c <= '9'); } static bool IsHexDigit(char c) { return IsDecimalDigit(c) || (('A' <= c) && (c <= 'F')) || (('a' <= c) && (c <= 'f')); } static int HexDigitToInt(char c) { ASSERT(IsHexDigit(c)); if (IsDecimalDigit(c)) return c - '0'; if (('A' <= c) && (c <= 'F')) return 10 + (c - 'A'); return 10 + (c - 'a'); } static char IntToHexDigit(int i) { ASSERT(0 <= i && i < 16); if (i < 10) return static_cast('0' + i); return static_cast('A' + (i - 10)); } // Perform a range check, checking if // offset + count <= length // without the risk of integer overflow. static inline bool RangeCheck(intptr_t offset, intptr_t count, intptr_t length) { return offset >= 0 && count >= 0 && length >= 0 && count <= (length - offset); } static inline bool WillAddOverflow(int64_t a, int64_t b) { return ((b > 0) && (a > (kMaxInt64 - b))) || ((b < 0) && (a < (kMinInt64 - b))); } static inline bool WillSubOverflow(int64_t a, int64_t b) { return ((b > 0) && (a < (kMinInt64 + b))) || ((b < 0) && (a > (kMaxInt64 + b))); } // Adds two int64_t values with wrapping around // (two's complement arithmetic). template static inline T AddWithWrapAround(T a, T b) { // Avoid undefined behavior by doing arithmetic in the unsigned type. using Unsigned = typename std::make_unsigned::type; return static_cast(static_cast(a) + static_cast(b)); } // Subtracts two int64_t values with wrapping around // (two's complement arithmetic). template static inline T SubWithWrapAround(T a, T b) { // Avoid undefined behavior by doing arithmetic in the unsigned type. using Unsigned = typename std::make_unsigned::type; return static_cast(static_cast(a) - static_cast(b)); } // Multiplies two int64_t values with wrapping around // (two's complement arithmetic). template static inline T MulWithWrapAround(T a, T b) { // Avoid undefined behavior by doing arithmetic in the unsigned type. using Unsigned = typename std::make_unsigned::type; return static_cast(static_cast(a) * static_cast(b)); } template static inline T NegWithWrapAround(T a) { // Avoid undefined behavior by doing arithmetic in the unsigned type. using Unsigned = typename std::make_unsigned::type; return static_cast(-static_cast(a)); } // Shifts int64_t value left. Supports any non-negative number of bits and // silently discards shifted out bits. static inline int64_t ShiftLeftWithTruncation(int64_t a, int64_t b) { ASSERT(b >= 0); if (b >= kBitsPerInt64) { return 0; } // Avoid undefined behavior by doing arithmetic in the unsigned type. return static_cast(static_cast(a) << b); } template static inline T RotateLeft(T value, uint8_t rotate) { const uint8_t width = sizeof(T) * kBitsPerByte; ASSERT(0 <= rotate); ASSERT(rotate <= width); using Unsigned = typename std::make_unsigned::type; return (static_cast(value) << rotate) | (static_cast(value) >> ((width - rotate) & (width - 1))); } template static inline T RotateRight(T value, uint8_t rotate) { const uint8_t width = sizeof(T) * kBitsPerByte; ASSERT(0 <= rotate); ASSERT(rotate <= width); using Unsigned = typename std::make_unsigned::type; return (static_cast(value) >> rotate) | (static_cast(value) << ((width - rotate) & (width - 1))); } NO_SANITIZE_UNDEFINED("float-divide-by-zero") static inline float DivideAllowZero(float a, float b) { return a / b; } NO_SANITIZE_UNDEFINED("float-divide-by-zero") static inline double DivideAllowZero(double a, double b) { return a / b; } // Utility functions for converting values from host endianness to // big or little endian values. static uint16_t HostToBigEndian16(uint16_t host_value); static uint32_t HostToBigEndian32(uint32_t host_value); static uint64_t HostToBigEndian64(uint64_t host_value); static uint16_t HostToLittleEndian16(uint16_t host_value); static uint32_t HostToLittleEndian32(uint32_t host_value); static uint64_t HostToLittleEndian64(uint64_t host_value); // Going between Host <-> LE/BE is the same operation for all practical // purposes. static inline uint32_t BigEndianToHost32(uint32_t be_value) { return HostToBigEndian32(be_value); } static inline uint64_t LittleEndianToHost64(uint64_t le_value) { return HostToLittleEndian64(le_value); } static bool DoublesBitEqual(const double a, const double b) { return bit_cast(a) == bit_cast(b); } // A double-to-integer conversion that avoids undefined behavior. // Out of range values and NaNs are converted to minimum value // for type T. template static T SafeDoubleToInt(double v) { const double min = static_cast(std::numeric_limits::min()); const double max = static_cast(std::numeric_limits::max()); return (min <= v && v <= max) ? static_cast(v) : std::numeric_limits::min(); } // dart2js represents integers as double precision floats, which can // represent anything in the range -2^53 ... 2^53. static bool IsJavaScriptInt(int64_t value) { return ((-0x20000000000000LL <= value) && (value <= 0x20000000000000LL)); } // The lowest n bits are 1, the others are 0. template static constexpr T NBitMask(size_t n) { using Unsigned = typename std::make_unsigned::type; constexpr size_t kBitsPerT = sizeof(T) * kBitsPerByte; assert(n <= sizeof(T) * kBitsPerT); return static_cast(n == kBitsPerT ? std::numeric_limits::max() : (static_cast(1) << n) - 1); } template static constexpr T Bit(size_t n) { ASSERT(n < sizeof(T) * kBitsPerByte); T bit = 1; return bit << n; } template static constexpr bool TestBit(T mask, size_t position) { ASSERT(position < sizeof(T) * kBitsPerByte); return ((mask >> position) & 1) != 0; } template class BitsIterator { public: explicit BitsIterator(uint32_t bits) : bits_(bits), bit_(bits & -bits) {} DART_FORCE_INLINE T operator*() const { return static_cast(BitPosition(bit_)); } DART_FORCE_INLINE bool operator==(const BitsIterator& other) const { return bits_ == other.bits_ && bit_ == other.bit_; } DART_FORCE_INLINE bool operator!=(const BitsIterator& other) const { return !(*this == other); } DART_FORCE_INLINE BitsIterator& operator++() { bits_ ^= bit_; bit_ = bits_ & -bits_; return *this; } private: // Returns position of the given bit. Unlike CountTrailingZeroes assumes // that bit is not zero without checking! static DART_FORCE_INLINE intptr_t BitPosition(uint32_t bit) { #if defined(DART_HOST_OS_WINDOWS) unsigned long position; // NOLINT BitScanForward(&position, bit); return static_cast(position); #else return __builtin_ctz(bit); #endif } uint32_t bits_; intptr_t bit_; }; template class BitsRange { public: explicit BitsRange(uint32_t bits) : bits_(bits) {} BitsIterator begin() { return BitsIterator(bits_); } BitsIterator end() { return BitsIterator(0); } public: const uint32_t bits_; }; static char* StrError(int err, char* buffer, size_t bufsize); // Not all platforms support strndup. static char* StrNDup(const char* s, intptr_t n); static char* StrDup(const char* s); static intptr_t StrNLen(const char* s, intptr_t n); static bool StrStartsWith(const char* s, const char* prefix) { return strncmp(s, prefix, strlen(prefix)) == 0; } static int Close(int fildes); static size_t Read(int filedes, void* buf, size_t nbyte); static int Unlink(const char* path); // Print formatted output info a buffer. // // Does not write more than size characters (including the trailing '\0'). // // Returns the number of characters (excluding the trailing '\0') // that would been written if the buffer had been big enough. If // the return value is greater or equal than the given size then the // output has been truncated. The return value is never negative. // // The buffer will always be terminated by a '\0', unless the buffer // is of size 0. The buffer might be nullptr if the size is 0. // // This specification conforms to C99 standard which is implemented // by glibc 2.1+ with one exception: the C99 standard allows a // negative return value. We will terminate the vm rather than let // that occur. static int SNPrint(char* str, size_t size, const char* format, ...) PRINTF_ATTRIBUTE(3, 4); static int VSNPrint(char* str, size_t size, const char* format, va_list args); // Allocate a string and print formatted output into a malloc'd buffer. static char* SCreate(const char* format, ...) PRINTF_ATTRIBUTE(1, 2); static char* VSCreate(const char* format, va_list args); // Load dynamic library from the given |library_path| and return the // library handle. |library_path| can be |nullptr| in which case // library handle representing the executable is returned. // If an error occurs returns |nullptr| and populates // |error| (if provided) with an error message (caller must free this message // when it is no longer needed). static void* LoadDynamicLibrary(const char* library_path, bool search_dll_load_dir = false, char** error = nullptr); static void* LoadDynamicLibrary(const char* library_path, char** error = nullptr) { return LoadDynamicLibrary(library_path, /*search_dll_load_dir=*/false, error); } // Resolve the given |symbol| within the library referenced by the // given |library_handle|. // If an error occurs populates |error| (if provided) with an error message // (caller must free this message when it is no longer needed). // Note: on some platforms |nullptr| is a valid value for a symbol, so to // check if resolution succeeded one must instead provide non-null |error| // and then check if it was populated with an error message. static void* ResolveSymbolInDynamicLibrary(void* library_handle, const char* symbol, char** error = nullptr); // Unload the library referenced by the given |library_handle|. // If an error occurs returns |nullptr| and populates // |error| (if provided) with an error message (caller must free this message // when it is no longer needed). static void UnloadDynamicLibrary(void* library_handle, char** error = nullptr); // Returns the basename of the given path. The returned string is malloced // and must be freed by the caller once no longer needed. // // If path is nullptr, returns nullptr. // // Returns nullptr if the operating system does not support this operation. static char* Basename(const char* path); #if defined(DART_HOST_OS_LINUX) static bool IsWindowsSubsystemForLinux(); #endif }; } // namespace dart #if defined(DART_HOST_OS_ANDROID) #include "platform/utils_android.h" #elif defined(DART_HOST_OS_FUCHSIA) #include "platform/utils_fuchsia.h" #elif defined(DART_HOST_OS_LINUX) #include "platform/utils_linux.h" #elif defined(DART_HOST_OS_MACOS) #include "platform/utils_macos.h" #elif defined(DART_HOST_OS_WINDOWS) #include "platform/utils_win.h" #else #error Unknown target os. #endif #endif // RUNTIME_PLATFORM_UTILS_H_