aboutsummaryrefslogtreecommitdiff
path: root/src/common/tuklib_integer.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/tuklib_integer.h')
-rw-r--r--src/common/tuklib_integer.h520
1 files changed, 364 insertions, 156 deletions
diff --git a/src/common/tuklib_integer.h b/src/common/tuklib_integer.h
index b1e84d5c1d0c..6f44a7a0ac22 100644
--- a/src/common/tuklib_integer.h
+++ b/src/common/tuklib_integer.h
@@ -6,22 +6,26 @@
/// This file provides macros or functions to do some basic integer and bit
/// operations.
///
-/// Endianness related integer operations (XX = 16, 32, or 64; Y = b or l):
-/// - Byte swapping: bswapXX(num)
-/// - Byte order conversions to/from native: convXXYe(num)
-/// - Aligned reads: readXXYe(ptr)
-/// - Aligned writes: writeXXYe(ptr, num)
-/// - Unaligned reads (16/32-bit only): unaligned_readXXYe(ptr)
-/// - Unaligned writes (16/32-bit only): unaligned_writeXXYe(ptr, num)
+/// Native endian inline functions (XX = 16, 32, or 64):
+/// - Unaligned native endian reads: readXXne(ptr)
+/// - Unaligned native endian writes: writeXXne(ptr, num)
+/// - Aligned native endian reads: aligned_readXXne(ptr)
+/// - Aligned native endian writes: aligned_writeXXne(ptr, num)
///
-/// Since they can macros, the arguments should have no side effects since
-/// they may be evaluated more than once.
+/// Endianness-converting integer operations (these can be macros!)
+/// (XX = 16, 32, or 64; Y = b or l):
+/// - Byte swapping: bswapXX(num)
+/// - Byte order conversions to/from native (byteswaps if Y isn't
+/// the native endianness): convXXYe(num)
+/// - Unaligned reads (16/32-bit only): readXXYe(ptr)
+/// - Unaligned writes (16/32-bit only): writeXXYe(ptr, num)
+/// - Aligned reads: aligned_readXXYe(ptr)
+/// - Aligned writes: aligned_writeXXYe(ptr, num)
///
-/// \todo PowerPC and possibly some other architectures support
-/// byte swapping load and store instructions. This file
-/// doesn't take advantage of those instructions.
+/// Since the above can macros, the arguments should have no side effects
+/// because they may be evaluated more than once.
///
-/// Bit scan operations for non-zero 32-bit integers:
+/// Bit scan operations for non-zero 32-bit integers (inline functions):
/// - Bit scan reverse (find highest non-zero bit): bsr32(num)
/// - Count leading zeros: clz32(num)
/// - Count trailing zeros: ctz32(num)
@@ -42,13 +46,26 @@
#define TUKLIB_INTEGER_H
#include "tuklib_common.h"
+#include <string.h>
+
+// Newer Intel C compilers require immintrin.h for _bit_scan_reverse()
+// and such functions.
+#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1500)
+# include <immintrin.h>
+#endif
-////////////////////////////////////////
-// Operating system specific features //
-////////////////////////////////////////
+///////////////////
+// Byte swapping //
+///////////////////
-#if defined(HAVE_BYTESWAP_H)
+#if defined(HAVE___BUILTIN_BSWAPXX)
+ // GCC >= 4.8 and Clang
+# define bswap16(n) __builtin_bswap16(n)
+# define bswap32(n) __builtin_bswap32(n)
+# define bswap64(n) __builtin_bswap64(n)
+
+#elif defined(HAVE_BYTESWAP_H)
// glibc, uClibc, dietlibc
# include <byteswap.h>
# ifdef HAVE_BSWAP_16
@@ -97,45 +114,33 @@
# endif
#endif
-
-////////////////////////////////
-// Compiler-specific features //
-////////////////////////////////
-
-// Newer Intel C compilers require immintrin.h for _bit_scan_reverse()
-// and such functions.
-#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 1500)
-# include <immintrin.h>
-#endif
-
-
-///////////////////
-// Byte swapping //
-///////////////////
-
#ifndef bswap16
-# define bswap16(num) \
- (((uint16_t)(num) << 8) | ((uint16_t)(num) >> 8))
+# define bswap16(n) (uint16_t)( \
+ (((n) & 0x00FFU) << 8) \
+ | (((n) & 0xFF00U) >> 8) \
+ )
#endif
#ifndef bswap32
-# define bswap32(num) \
- ( (((uint32_t)(num) << 24) ) \
- | (((uint32_t)(num) << 8) & UINT32_C(0x00FF0000)) \
- | (((uint32_t)(num) >> 8) & UINT32_C(0x0000FF00)) \
- | (((uint32_t)(num) >> 24) ) )
+# define bswap32(n) (uint32_t)( \
+ (((n) & UINT32_C(0x000000FF)) << 24) \
+ | (((n) & UINT32_C(0x0000FF00)) << 8) \
+ | (((n) & UINT32_C(0x00FF0000)) >> 8) \
+ | (((n) & UINT32_C(0xFF000000)) >> 24) \
+ )
#endif
#ifndef bswap64
-# define bswap64(num) \
- ( (((uint64_t)(num) << 56) ) \
- | (((uint64_t)(num) << 40) & UINT64_C(0x00FF000000000000)) \
- | (((uint64_t)(num) << 24) & UINT64_C(0x0000FF0000000000)) \
- | (((uint64_t)(num) << 8) & UINT64_C(0x000000FF00000000)) \
- | (((uint64_t)(num) >> 8) & UINT64_C(0x00000000FF000000)) \
- | (((uint64_t)(num) >> 24) & UINT64_C(0x0000000000FF0000)) \
- | (((uint64_t)(num) >> 40) & UINT64_C(0x000000000000FF00)) \
- | (((uint64_t)(num) >> 56) ) )
+# define bswap64(n) (uint64_t)( \
+ (((n) & UINT64_C(0x00000000000000FF)) << 56) \
+ | (((n) & UINT64_C(0x000000000000FF00)) << 40) \
+ | (((n) & UINT64_C(0x0000000000FF0000)) << 24) \
+ | (((n) & UINT64_C(0x00000000FF000000)) << 8) \
+ | (((n) & UINT64_C(0x000000FF00000000)) >> 8) \
+ | (((n) & UINT64_C(0x0000FF0000000000)) >> 24) \
+ | (((n) & UINT64_C(0x00FF000000000000)) >> 40) \
+ | (((n) & UINT64_C(0xFF00000000000000)) >> 56) \
+ )
#endif
// Define conversion macros using the basic byte swapping macros.
@@ -180,76 +185,76 @@
#endif
-//////////////////////////////
-// Aligned reads and writes //
-//////////////////////////////
-
-static inline uint16_t
-read16be(const uint8_t *buf)
-{
- uint16_t num = *(const uint16_t *)buf;
- return conv16be(num);
-}
+////////////////////////////////
+// Unaligned reads and writes //
+////////////////////////////////
+// The traditional way of casting e.g. *(const uint16_t *)uint8_pointer
+// is bad even if the uint8_pointer is properly aligned because this kind
+// of casts break strict aliasing rules and result in undefined behavior.
+// With unaligned pointers it's even worse: compilers may emit vector
+// instructions that require aligned pointers even if non-vector
+// instructions work with unaligned pointers.
+//
+// Using memcpy() is the standard compliant way to do unaligned access.
+// Many modern compilers inline it so there is no function call overhead.
+// For those compilers that don't handle the memcpy() method well, the
+// old casting method (that violates strict aliasing) can be requested at
+// build time. A third method, casting to a packed struct, would also be
+// an option but isn't provided to keep things simpler (it's already a mess).
+// Hopefully this is flexible enough in practice.
static inline uint16_t
-read16le(const uint8_t *buf)
+read16ne(const uint8_t *buf)
{
- uint16_t num = *(const uint16_t *)buf;
- return conv16le(num);
-}
-
-
-static inline uint32_t
-read32be(const uint8_t *buf)
-{
- uint32_t num = *(const uint32_t *)buf;
- return conv32be(num);
+#if defined(TUKLIB_FAST_UNALIGNED_ACCESS) \
+ && defined(TUKLIB_USE_UNSAFE_TYPE_PUNNING)
+ return *(const uint16_t *)buf;
+#else
+ uint16_t num;
+ memcpy(&num, buf, sizeof(num));
+ return num;
+#endif
}
static inline uint32_t
-read32le(const uint8_t *buf)
-{
- uint32_t num = *(const uint32_t *)buf;
- return conv32le(num);
-}
-
-
-static inline uint64_t
-read64be(const uint8_t *buf)
+read32ne(const uint8_t *buf)
{
- uint64_t num = *(const uint64_t *)buf;
- return conv64be(num);
+#if defined(TUKLIB_FAST_UNALIGNED_ACCESS) \
+ && defined(TUKLIB_USE_UNSAFE_TYPE_PUNNING)
+ return *(const uint32_t *)buf;
+#else
+ uint32_t num;
+ memcpy(&num, buf, sizeof(num));
+ return num;
+#endif
}
static inline uint64_t
-read64le(const uint8_t *buf)
+read64ne(const uint8_t *buf)
{
- uint64_t num = *(const uint64_t *)buf;
- return conv64le(num);
+#if defined(TUKLIB_FAST_UNALIGNED_ACCESS) \
+ && defined(TUKLIB_USE_UNSAFE_TYPE_PUNNING)
+ return *(const uint64_t *)buf;
+#else
+ uint64_t num;
+ memcpy(&num, buf, sizeof(num));
+ return num;
+#endif
}
-// NOTE: Possible byte swapping must be done in a macro to allow GCC
-// to optimize byte swapping of constants when using glibc's or *BSD's
-// byte swapping macros. The actual write is done in an inline function
-// to make type checking of the buf pointer possible similarly to readXXYe()
-// functions.
-
-#define write16be(buf, num) write16ne((buf), conv16be(num))
-#define write16le(buf, num) write16ne((buf), conv16le(num))
-#define write32be(buf, num) write32ne((buf), conv32be(num))
-#define write32le(buf, num) write32ne((buf), conv32le(num))
-#define write64be(buf, num) write64ne((buf), conv64be(num))
-#define write64le(buf, num) write64ne((buf), conv64le(num))
-
-
static inline void
write16ne(uint8_t *buf, uint16_t num)
{
+#if defined(TUKLIB_FAST_UNALIGNED_ACCESS) \
+ && defined(TUKLIB_USE_UNSAFE_TYPE_PUNNING)
*(uint16_t *)buf = num;
+#else
+ memcpy(buf, &num, sizeof(num));
+#endif
return;
}
@@ -257,7 +262,12 @@ write16ne(uint8_t *buf, uint16_t num)
static inline void
write32ne(uint8_t *buf, uint32_t num)
{
+#if defined(TUKLIB_FAST_UNALIGNED_ACCESS) \
+ && defined(TUKLIB_USE_UNSAFE_TYPE_PUNNING)
*(uint32_t *)buf = num;
+#else
+ memcpy(buf, &num, sizeof(num));
+#endif
return;
}
@@ -265,90 +275,114 @@ write32ne(uint8_t *buf, uint32_t num)
static inline void
write64ne(uint8_t *buf, uint64_t num)
{
+#if defined(TUKLIB_FAST_UNALIGNED_ACCESS) \
+ && defined(TUKLIB_USE_UNSAFE_TYPE_PUNNING)
*(uint64_t *)buf = num;
+#else
+ memcpy(buf, &num, sizeof(num));
+#endif
return;
}
-////////////////////////////////
-// Unaligned reads and writes //
-////////////////////////////////
-
-// NOTE: TUKLIB_FAST_UNALIGNED_ACCESS indicates only support for 16-bit and
-// 32-bit unaligned integer loads and stores. It's possible that 64-bit
-// unaligned access doesn't work or is slower than byte-by-byte access.
-// Since unaligned 64-bit is probably not needed as often as 16-bit or
-// 32-bit, we simply don't support 64-bit unaligned access for now.
-#ifdef TUKLIB_FAST_UNALIGNED_ACCESS
-# define unaligned_read16be read16be
-# define unaligned_read16le read16le
-# define unaligned_read32be read32be
-# define unaligned_read32le read32le
-# define unaligned_write16be write16be
-# define unaligned_write16le write16le
-# define unaligned_write32be write32be
-# define unaligned_write32le write32le
-
-#else
-
static inline uint16_t
-unaligned_read16be(const uint8_t *buf)
+read16be(const uint8_t *buf)
{
+#if defined(WORDS_BIGENDIAN) || defined(TUKLIB_FAST_UNALIGNED_ACCESS)
+ uint16_t num = read16ne(buf);
+ return conv16be(num);
+#else
uint16_t num = ((uint16_t)buf[0] << 8) | (uint16_t)buf[1];
return num;
+#endif
}
static inline uint16_t
-unaligned_read16le(const uint8_t *buf)
+read16le(const uint8_t *buf)
{
+#if !defined(WORDS_BIGENDIAN) || defined(TUKLIB_FAST_UNALIGNED_ACCESS)
+ uint16_t num = read16ne(buf);
+ return conv16le(num);
+#else
uint16_t num = ((uint16_t)buf[0]) | ((uint16_t)buf[1] << 8);
return num;
+#endif
}
static inline uint32_t
-unaligned_read32be(const uint8_t *buf)
+read32be(const uint8_t *buf)
{
+#if defined(WORDS_BIGENDIAN) || defined(TUKLIB_FAST_UNALIGNED_ACCESS)
+ uint32_t num = read32ne(buf);
+ return conv32be(num);
+#else
uint32_t num = (uint32_t)buf[0] << 24;
num |= (uint32_t)buf[1] << 16;
num |= (uint32_t)buf[2] << 8;
num |= (uint32_t)buf[3];
return num;
+#endif
}
static inline uint32_t
-unaligned_read32le(const uint8_t *buf)
+read32le(const uint8_t *buf)
{
+#if !defined(WORDS_BIGENDIAN) || defined(TUKLIB_FAST_UNALIGNED_ACCESS)
+ uint32_t num = read32ne(buf);
+ return conv32le(num);
+#else
uint32_t num = (uint32_t)buf[0];
num |= (uint32_t)buf[1] << 8;
num |= (uint32_t)buf[2] << 16;
num |= (uint32_t)buf[3] << 24;
return num;
+#endif
}
+// NOTE: Possible byte swapping must be done in a macro to allow the compiler
+// to optimize byte swapping of constants when using glibc's or *BSD's
+// byte swapping macros. The actual write is done in an inline function
+// to make type checking of the buf pointer possible.
+#if defined(WORDS_BIGENDIAN) || defined(TUKLIB_FAST_UNALIGNED_ACCESS)
+# define write16be(buf, num) write16ne(buf, conv16be(num))
+# define write32be(buf, num) write32ne(buf, conv32be(num))
+#endif
+
+#if !defined(WORDS_BIGENDIAN) || defined(TUKLIB_FAST_UNALIGNED_ACCESS)
+# define write16le(buf, num) write16ne(buf, conv16le(num))
+# define write32le(buf, num) write32ne(buf, conv32le(num))
+#endif
+
+
+#ifndef write16be
static inline void
-unaligned_write16be(uint8_t *buf, uint16_t num)
+write16be(uint8_t *buf, uint16_t num)
{
buf[0] = (uint8_t)(num >> 8);
buf[1] = (uint8_t)num;
return;
}
+#endif
+#ifndef write16le
static inline void
-unaligned_write16le(uint8_t *buf, uint16_t num)
+write16le(uint8_t *buf, uint16_t num)
{
buf[0] = (uint8_t)num;
buf[1] = (uint8_t)(num >> 8);
return;
}
+#endif
+#ifndef write32be
static inline void
-unaligned_write32be(uint8_t *buf, uint32_t num)
+write32be(uint8_t *buf, uint32_t num)
{
buf[0] = (uint8_t)(num >> 24);
buf[1] = (uint8_t)(num >> 16);
@@ -356,10 +390,12 @@ unaligned_write32be(uint8_t *buf, uint32_t num)
buf[3] = (uint8_t)num;
return;
}
+#endif
+#ifndef write32le
static inline void
-unaligned_write32le(uint8_t *buf, uint32_t num)
+write32le(uint8_t *buf, uint32_t num)
{
buf[0] = (uint8_t)num;
buf[1] = (uint8_t)(num >> 8);
@@ -367,10 +403,184 @@ unaligned_write32le(uint8_t *buf, uint32_t num)
buf[3] = (uint8_t)(num >> 24);
return;
}
+#endif
+
+
+//////////////////////////////
+// Aligned reads and writes //
+//////////////////////////////
+// Separate functions for aligned reads and writes are provided since on
+// strict-align archs aligned access is much faster than unaligned access.
+//
+// Just like in the unaligned case, memcpy() is needed to avoid
+// strict aliasing violations. However, on archs that don't support
+// unaligned access the compiler cannot know that the pointers given
+// to memcpy() are aligned which results in slow code. As of C11 there is
+// no standard way to tell the compiler that we know that the address is
+// aligned but some compilers have language extensions to do that. With
+// such language extensions the memcpy() method gives excellent results.
+//
+// What to do on a strict-align system when no known language extentensions
+// are available? Falling back to byte-by-byte access would be safe but ruin
+// optimizations that have been made specifically with aligned access in mind.
+// As a compromise, aligned reads will fall back to non-compliant type punning
+// but aligned writes will be byte-by-byte, that is, fast reads are preferred
+// over fast writes. This obviously isn't great but hopefully it's a working
+// compromise for now.
+//
+// __builtin_assume_aligned is support by GCC >= 4.7 and clang >= 3.6.
+#ifdef HAVE___BUILTIN_ASSUME_ALIGNED
+# define tuklib_memcpy_aligned(dest, src, size) \
+ memcpy(dest, __builtin_assume_aligned(src, size), size)
+#else
+# define tuklib_memcpy_aligned(dest, src, size) \
+ memcpy(dest, src, size)
+# ifndef TUKLIB_FAST_UNALIGNED_ACCESS
+# define TUKLIB_USE_UNSAFE_ALIGNED_READS 1
+# endif
#endif
+static inline uint16_t
+aligned_read16ne(const uint8_t *buf)
+{
+#if defined(TUKLIB_USE_UNSAFE_TYPE_PUNNING) \
+ || defined(TUKLIB_USE_UNSAFE_ALIGNED_READS)
+ return *(const uint16_t *)buf;
+#else
+ uint16_t num;
+ tuklib_memcpy_aligned(&num, buf, sizeof(num));
+ return num;
+#endif
+}
+
+
+static inline uint32_t
+aligned_read32ne(const uint8_t *buf)
+{
+#if defined(TUKLIB_USE_UNSAFE_TYPE_PUNNING) \
+ || defined(TUKLIB_USE_UNSAFE_ALIGNED_READS)
+ return *(const uint32_t *)buf;
+#else
+ uint32_t num;
+ tuklib_memcpy_aligned(&num, buf, sizeof(num));
+ return num;
+#endif
+}
+
+
+static inline uint64_t
+aligned_read64ne(const uint8_t *buf)
+{
+#if defined(TUKLIB_USE_UNSAFE_TYPE_PUNNING) \
+ || defined(TUKLIB_USE_UNSAFE_ALIGNED_READS)
+ return *(const uint64_t *)buf;
+#else
+ uint64_t num;
+ tuklib_memcpy_aligned(&num, buf, sizeof(num));
+ return num;
+#endif
+}
+
+
+static inline void
+aligned_write16ne(uint8_t *buf, uint16_t num)
+{
+#ifdef TUKLIB_USE_UNSAFE_TYPE_PUNNING
+ *(uint16_t *)buf = num;
+#else
+ tuklib_memcpy_aligned(buf, &num, sizeof(num));
+#endif
+ return;
+}
+
+
+static inline void
+aligned_write32ne(uint8_t *buf, uint32_t num)
+{
+#ifdef TUKLIB_USE_UNSAFE_TYPE_PUNNING
+ *(uint32_t *)buf = num;
+#else
+ tuklib_memcpy_aligned(buf, &num, sizeof(num));
+#endif
+ return;
+}
+
+
+static inline void
+aligned_write64ne(uint8_t *buf, uint64_t num)
+{
+#ifdef TUKLIB_USE_UNSAFE_TYPE_PUNNING
+ *(uint64_t *)buf = num;
+#else
+ tuklib_memcpy_aligned(buf, &num, sizeof(num));
+#endif
+ return;
+}
+
+
+static inline uint16_t
+aligned_read16be(const uint8_t *buf)
+{
+ uint16_t num = aligned_read16ne(buf);
+ return conv16be(num);
+}
+
+
+static inline uint16_t
+aligned_read16le(const uint8_t *buf)
+{
+ uint16_t num = aligned_read16ne(buf);
+ return conv16le(num);
+}
+
+
+static inline uint32_t
+aligned_read32be(const uint8_t *buf)
+{
+ uint32_t num = aligned_read32ne(buf);
+ return conv32be(num);
+}
+
+
+static inline uint32_t
+aligned_read32le(const uint8_t *buf)
+{
+ uint32_t num = aligned_read32ne(buf);
+ return conv32le(num);
+}
+
+
+static inline uint64_t
+aligned_read64be(const uint8_t *buf)
+{
+ uint64_t num = aligned_read64ne(buf);
+ return conv64be(num);
+}
+
+
+static inline uint64_t
+aligned_read64le(const uint8_t *buf)
+{
+ uint64_t num = aligned_read64ne(buf);
+ return conv64le(num);
+}
+
+
+// These need to be macros like in the unaligned case.
+#define aligned_write16be(buf, num) aligned_write16ne((buf), conv16be(num))
+#define aligned_write16le(buf, num) aligned_write16ne((buf), conv16le(num))
+#define aligned_write32be(buf, num) aligned_write32ne((buf), conv32be(num))
+#define aligned_write32le(buf, num) aligned_write32ne((buf), conv32le(num))
+#define aligned_write64be(buf, num) aligned_write64ne((buf), conv64be(num))
+#define aligned_write64le(buf, num) aligned_write64ne((buf), conv64le(num))
+
+
+////////////////////
+// Bit operations //
+////////////////////
+
static inline uint32_t
bsr32(uint32_t n)
{
@@ -383,44 +593,42 @@ bsr32(uint32_t n)
// multiple architectures. On x86, __builtin_clz() ^ 31U becomes
// either plain BSR (so the XOR gets optimized away) or LZCNT and
// XOR (if -march indicates that SSE4a instructions are supported).
- return __builtin_clz(n) ^ 31U;
+ return (uint32_t)__builtin_clz(n) ^ 31U;
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
uint32_t i;
__asm__("bsrl %1, %0" : "=r" (i) : "rm" (n));
return i;
-#elif defined(_MSC_VER) && _MSC_VER >= 1400
- // MSVC isn't supported by tuklib, but since this code exists,
- // it doesn't hurt to have it here anyway.
- uint32_t i;
- _BitScanReverse((DWORD *)&i, n);
+#elif defined(_MSC_VER)
+ unsigned long i;
+ _BitScanReverse(&i, n);
return i;
#else
uint32_t i = 31;
- if ((n & UINT32_C(0xFFFF0000)) == 0) {
+ if ((n & 0xFFFF0000) == 0) {
n <<= 16;
i = 15;
}
- if ((n & UINT32_C(0xFF000000)) == 0) {
+ if ((n & 0xFF000000) == 0) {
n <<= 8;
i -= 8;
}
- if ((n & UINT32_C(0xF0000000)) == 0) {
+ if ((n & 0xF0000000) == 0) {
n <<= 4;
i -= 4;
}
- if ((n & UINT32_C(0xC0000000)) == 0) {
+ if ((n & 0xC0000000) == 0) {
n <<= 2;
i -= 2;
}
- if ((n & UINT32_C(0x80000000)) == 0)
+ if ((n & 0x80000000) == 0)
--i;
return i;
@@ -435,7 +643,7 @@ clz32(uint32_t n)
return _bit_scan_reverse(n) ^ 31U;
#elif TUKLIB_GNUC_REQ(3, 4) && UINT_MAX == UINT32_MAX
- return __builtin_clz(n);
+ return (uint32_t)__builtin_clz(n);
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
uint32_t i;
@@ -444,35 +652,35 @@ clz32(uint32_t n)
: "=r" (i) : "rm" (n));
return i;
-#elif defined(_MSC_VER) && _MSC_VER >= 1400
- uint32_t i;
- _BitScanReverse((DWORD *)&i, n);
+#elif defined(_MSC_VER)
+ unsigned long i;
+ _BitScanReverse(&i, n);
return i ^ 31U;
#else
uint32_t i = 0;
- if ((n & UINT32_C(0xFFFF0000)) == 0) {
+ if ((n & 0xFFFF0000) == 0) {
n <<= 16;
i = 16;
}
- if ((n & UINT32_C(0xFF000000)) == 0) {
+ if ((n & 0xFF000000) == 0) {
n <<= 8;
i += 8;
}
- if ((n & UINT32_C(0xF0000000)) == 0) {
+ if ((n & 0xF0000000) == 0) {
n <<= 4;
i += 4;
}
- if ((n & UINT32_C(0xC0000000)) == 0) {
+ if ((n & 0xC0000000) == 0) {
n <<= 2;
i += 2;
}
- if ((n & UINT32_C(0x80000000)) == 0)
+ if ((n & 0x80000000) == 0)
++i;
return i;
@@ -487,42 +695,42 @@ ctz32(uint32_t n)
return _bit_scan_forward(n);
#elif TUKLIB_GNUC_REQ(3, 4) && UINT_MAX >= UINT32_MAX
- return __builtin_ctz(n);
+ return (uint32_t)__builtin_ctz(n);
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
uint32_t i;
__asm__("bsfl %1, %0" : "=r" (i) : "rm" (n));
return i;
-#elif defined(_MSC_VER) && _MSC_VER >= 1400
- uint32_t i;
- _BitScanForward((DWORD *)&i, n);
+#elif defined(_MSC_VER)
+ unsigned long i;
+ _BitScanForward(&i, n);
return i;
#else
uint32_t i = 0;
- if ((n & UINT32_C(0x0000FFFF)) == 0) {
+ if ((n & 0x0000FFFF) == 0) {
n >>= 16;
i = 16;
}
- if ((n & UINT32_C(0x000000FF)) == 0) {
+ if ((n & 0x000000FF) == 0) {
n >>= 8;
i += 8;
}
- if ((n & UINT32_C(0x0000000F)) == 0) {
+ if ((n & 0x0000000F) == 0) {
n >>= 4;
i += 4;
}
- if ((n & UINT32_C(0x00000003)) == 0) {
+ if ((n & 0x00000003) == 0) {
n >>= 2;
i += 2;
}
- if ((n & UINT32_C(0x00000001)) == 0)
+ if ((n & 0x00000001) == 0)
++i;
return i;