Update tuklib_integer.h with bit scan functions.

Thanks to Joachim Henke for the original patch.
This commit is contained in:
Lasse Collin 2009-11-22 11:52:30 +02:00
parent c74c132f7f
commit 7ac3985d89
1 changed files with 181 additions and 8 deletions

View File

@ -1,10 +1,12 @@
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// //
/// \file tuklib_integer.h /// \file tuklib_integer.h
/// \brief Byte swapping and endianness related macros and functions /// \brief Various integer and bit operations
/// ///
/// This file provides macros or functions to do basic endianness related /// This file provides macros or functions to do some basic integer and bit
/// integer operations (XX = 16, 32, or 64; Y = b or l): /// operations.
///
/// Endianness related integer operations (XX = 16, 32, or 64; Y = b or l):
/// - Byte swapping: bswapXX(num) /// - Byte swapping: bswapXX(num)
/// - Byte order conversions to/from native: convXXYe(num) /// - Byte order conversions to/from native: convXXYe(num)
/// - Aligned reads: readXXYe(ptr) /// - Aligned reads: readXXYe(ptr)
@ -18,8 +20,18 @@
/// \todo PowerPC and possibly some other architectures support /// \todo PowerPC and possibly some other architectures support
/// byte swapping load and store instructions. This file /// byte swapping load and store instructions. This file
/// doesn't take advantage of those instructions. /// doesn't take advantage of those instructions.
///
/// Bit scan operations for non-zero 32-bit integers:
/// - Bit scan reverse (find highest non-zero bit): bsr32(num)
/// - Count leading zeros: clz32(num)
/// - Count trailing zeros: ctz32(num)
/// - Bit scan forward (simply an alias for ctz32()): bsf32(num)
///
/// The above bit scan operations return 0-31. If num is zero,
/// the result is undefined.
// //
// Author: Lasse Collin // Authors: Lasse Collin
// Joachim Henke
// //
// This file has been put into the public domain. // This file has been put into the public domain.
// You can do whatever you want with this file. // You can do whatever you want with this file.
@ -213,8 +225,7 @@ read64le(const uint8_t *buf)
// to optimize byte swapping of constants when using glibc's or *BSD's // 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 // byte swapping macros. The actual write is done in an inline function
// to make type checking of the buf pointer possible similarly to readXXYe() // to make type checking of the buf pointer possible similarly to readXXYe()
// functions. This also seems to silence a probably bogus GCC warning about // functions.
// strict aliasing when buf points to the beginning of an uint8_t array.
#define write16be(buf, num) write16ne((buf), conv16be(num)) #define write16be(buf, num) write16ne((buf), conv16be(num))
#define write16le(buf, num) write16ne((buf), conv16le(num)) #define write16le(buf, num) write16ne((buf), conv16le(num))
@ -272,7 +283,7 @@ write64ne(uint8_t *buf, uint64_t num)
static inline uint16_t static inline uint16_t
unaligned_read16be(const uint8_t *buf) unaligned_read16be(const uint8_t *buf)
{ {
uint16_t num = ((uint16_t)buf[0] << 8) | buf[1]; uint16_t num = ((uint16_t)buf[0] << 8) | (uint16_t)buf[1];
return num; return num;
} }
@ -280,7 +291,7 @@ unaligned_read16be(const uint8_t *buf)
static inline uint16_t static inline uint16_t
unaligned_read16le(const uint8_t *buf) unaligned_read16le(const uint8_t *buf)
{ {
uint16_t num = ((uint32_t)buf[0]) | ((uint16_t)buf[1] << 8); uint16_t num = ((uint16_t)buf[0]) | ((uint16_t)buf[1] << 8);
return num; return num;
} }
@ -347,4 +358,166 @@ unaligned_write32le(uint8_t *buf, uint32_t num)
} }
#endif #endif
static inline uint32_t
bsr32(uint32_t n)
{
// Check for ICC first, since it tends to define __GNUC__ too.
#if defined(__INTEL_COMPILER)
return _bit_scan_reverse(n);
#elif TUKLIB_GNUC_REQ(3, 4) && UINT_MAX == UINT32_MAX
// GCC >= 3.4 has __builtin_clz(), which gives good results on
// 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;
#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);
return i;
#else
uint32_t i = 31;
if ((n & UINT32_C(0xFFFF0000)) == 0) {
n <<= 16;
i = 15;
}
if ((n & UINT32_C(0xFF000000)) == 0) {
n <<= 8;
i -= 8;
}
if ((n & UINT32_C(0xF0000000)) == 0) {
n <<= 4;
i -= 4;
}
if ((n & UINT32_C(0xC0000000)) == 0) {
n <<= 2;
i -= 2;
}
if ((n & UINT32_C(0x80000000)) == 0)
--i;
return i;
#endif
}
static inline uint32_t
clz32(uint32_t n)
{
#if defined(__INTEL_COMPILER)
return _bit_scan_reverse(n) ^ 31U;
#elif TUKLIB_GNUC_REQ(3, 4) && UINT_MAX == UINT32_MAX
return __builtin_clz(n);
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
uint32_t i;
__asm__("bsrl %1, %0\n\t"
"xorl $31, %0"
: "=r" (i) : "rm" (n));
return i;
#elif defined(_MSC_VER) && _MSC_VER >= 1400
uint32_t i;
_BitScanReverse((DWORD *)&i, n);
return i ^ 31U;
#else
uint32_t i = 0;
if ((n & UINT32_C(0xFFFF0000)) == 0) {
n <<= 16;
i = 16;
}
if ((n & UINT32_C(0xFF000000)) == 0) {
n <<= 8;
i += 8;
}
if ((n & UINT32_C(0xF0000000)) == 0) {
n <<= 4;
i += 4;
}
if ((n & UINT32_C(0xC0000000)) == 0) {
n <<= 2;
i += 2;
}
if ((n & UINT32_C(0x80000000)) == 0)
++i;
return i;
#endif
}
static inline uint32_t
ctz32(uint32_t n)
{
#if defined(__INTEL_COMPILER)
return _bit_scan_forward(n);
#elif TUKLIB_GNUC_REQ(3, 4) && UINT_MAX >= UINT32_MAX
return __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);
return i;
#else
uint32_t i = 0;
if ((n & UINT32_C(0x0000FFFF)) == 0) {
n >>= 16;
i = 16;
}
if ((n & UINT32_C(0x000000FF)) == 0) {
n >>= 8;
i += 8;
}
if ((n & UINT32_C(0x0000000F)) == 0) {
n >>= 4;
i += 4;
}
if ((n & UINT32_C(0x00000003)) == 0) {
n >>= 2;
i += 2;
}
if ((n & UINT32_C(0x00000001)) == 0)
++i;
return i;
#endif
}
#define bsf32 ctz32
#endif #endif