Adds qFindFirstSetBit() and qFindLastSetBit().

Two new function families have been added: qFindFirstSetBit() and
qFindLastSetBit() for a variety of integer sizes.  Fast implementations
are included for most platforms.

[ChangeLog][QtCore][QtAlgorithms] Added qFindFirstSetBit() and
qFindLastSetBit().

Change-Id: I89d9d1637ea26070aee5a60be95be1b51bfc84dc
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Glen Mabey 2015-02-07 21:11:22 -06:00
parent 5e9f7b9579
commit 046f325483
3 changed files with 302 additions and 0 deletions

125
src/corelib/tools/qalgorithms.h Normal file → Executable file
View File

@ -584,6 +584,131 @@ Q_DECL_CONST_FUNCTION Q_DECL_CONSTEXPR inline uint qPopulationCount(long unsigne
#undef QALGORITHMS_USE_BUILTIN_POPCOUNT
#endif
Q_DECL_RELAXED_CONSTEXPR inline uint qCountTrailingZeroBits(quint32 v) Q_DECL_NOTHROW
{
#if defined(Q_CC_GNU)
return v ? __builtin_ctz(v) : 32U;
#else
// see http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightParallel
unsigned int c = 32; // c will be the number of zero bits on the right
v &= -signed(v);
if (v) c--;
if (v & 0x0000FFFF) c -= 16;
if (v & 0x00FF00FF) c -= 8;
if (v & 0x0F0F0F0F) c -= 4;
if (v & 0x33333333) c -= 2;
if (v & 0x55555555) c -= 1;
return c;
#endif
}
Q_DECL_RELAXED_CONSTEXPR inline uint qCountTrailingZeroBits(quint8 v) Q_DECL_NOTHROW
{
#if defined(Q_CC_GNU)
return v ? __builtin_ctz(v) : 8U;
#else
unsigned int c = 8; // c will be the number of zero bits on the right
v &= -signed(v);
if (v) c--;
if (v & 0x0000000F) c -= 4;
if (v & 0x00000033) c -= 2;
if (v & 0x00000055) c -= 1;
return c;
#endif
}
Q_DECL_RELAXED_CONSTEXPR inline uint qCountTrailingZeroBits(quint16 v) Q_DECL_NOTHROW
{
#if defined(Q_CC_GNU)
return v ? __builtin_ctz(v) : 16U;
#else
unsigned int c = 16; // c will be the number of zero bits on the right
v &= -signed(v);
if (v) c--;
if (v & 0x000000FF) c -= 8;
if (v & 0x00000F0F) c -= 4;
if (v & 0x00003333) c -= 2;
if (v & 0x00005555) c -= 1;
return c;
#endif
}
Q_DECL_RELAXED_CONSTEXPR inline uint qCountTrailingZeroBits(quint64 v) Q_DECL_NOTHROW
{
#if defined(Q_CC_GNU)
return v ? __builtin_ctzll(v) : 64;
#else
quint32 x = static_cast<quint32>(v);
return x ? qCountTrailingZeroBits(x)
: 32 + qCountTrailingZeroBits(static_cast<quint32>(v >> 32));
#endif
}
Q_DECL_RELAXED_CONSTEXPR inline uint qCountTrailingZeroBits(unsigned long v) Q_DECL_NOTHROW
{
return qCountTrailingZeroBits(QIntegerForSizeof<long>::Unsigned(v));
}
Q_DECL_CONSTEXPR inline uint qCountLeadingZeroBits(quint32 v) Q_DECL_NOTHROW
{
#if defined(Q_CC_GNU)
return v ? __builtin_clz(v) : 32U;
#else
// Hacker's Delight, 2nd ed. Fig 5-16, p. 102
v = v | (v >> 1);
v = v | (v >> 2);
v = v | (v >> 4);
v = v | (v >> 8);
v = v | (v >> 16);
return qPopulationCount(~v);
#endif
}
Q_DECL_CONSTEXPR inline uint qCountLeadingZeroBits(quint8 v) Q_DECL_NOTHROW
{
#if defined(Q_CC_GNU)
return v ? __builtin_clz(v)-24U : 8U;
#else
v = v | (v >> 1);
v = v | (v >> 2);
v = v | (v >> 4);
return qPopulationCount(static_cast<quint8>(~v));
#endif
}
Q_DECL_CONSTEXPR inline uint qCountLeadingZeroBits(quint16 v) Q_DECL_NOTHROW
{
#if defined(Q_CC_GNU)
return v ? __builtin_clz(v)-16U : 16U;
#else
v = v | (v >> 1);
v = v | (v >> 2);
v = v | (v >> 4);
v = v | (v >> 8);
return qPopulationCount(static_cast<quint16>(~v));
#endif
}
Q_DECL_CONSTEXPR inline uint qCountLeadingZeroBits(quint64 v) Q_DECL_NOTHROW
{
#if defined(Q_CC_GNU)
return v ? __builtin_clzll(v) : 64U;
#else
v = v | (v >> 1);
v = v | (v >> 2);
v = v | (v >> 4);
v = v | (v >> 8);
v = v | (v >> 16);
v = v | (v >> 32);
return qPopulationCount(~v);
#endif
}
Q_DECL_CONSTEXPR inline uint qCountLeadingZeroBits(unsigned long v) Q_DECL_NOTHROW
{
return qCountLeadingZeroBits(QIntegerForSizeof<long>::Unsigned(v));
}
QT_WARNING_POP
QT_END_NAMESPACE

70
src/corelib/tools/qalgorithms.qdoc Normal file → Executable file
View File

@ -785,3 +785,73 @@
\since 5.2
\overload
*/
/*!
\fn uint qCountTrailingZeroBits(quint8 v)
\relates <QtAlgorithms>
\since 5.6
Returns the number of consecutive zero bits in \a v, when searching from the LSB.
For example, qCountTrailingZeroBits(1) returns 0 and qCountTrailingZeroBits(8) returns 3.
*/
/*!
\fn uint qCountTrailingZeroBits(quint16 v)
\relates <QtAlgorithms>
\since 5.6
\overload
*/
/*!
\fn uint qCountTrailingZeroBits(quint32 v)
\relates <QtAlgorithms>
\since 5.6
\overload
*/
/*!
\fn uint qCountTrailingZeroBits(quint64 v)
\relates <QtAlgorithms>
\since 5.6
\overload
*/
/*!
\fn uint qCountLeadingZeroBits(quint8 v)
\relates <QtAlgorithms>
\since 5.6
Returns the number of consecutive zero bits in \a v, when searching from the MSB.
For example, qCountLeadingZeroBits(quint8(1)) returns 7 and
qCountLeadingZeroBits(quint8(8)) returns 4.
*/
/*!
\fn uint qCountLeadingZeroBits(quint16 v)
\relates <QtAlgorithms>
\since 5.6
Returns the number of consecutive zero bits in \a v, when searching from the MSB.
For example, qCountLeadingZeroBits(quint16(1)) returns 15 and
qCountLeadingZeroBits(quint16(8)) returns 12.
*/
/*!
\fn uint qCountLeadingZeroBits(quint32 v)
\relates <QtAlgorithms>
\since 5.6
Returns the number of consecutive zero bits in \a v, when searching from the MSB.
For example, qCountLeadingZeroBits(quint32(1)) returns 31 and
qCountLeadingZeroBits(quint32(8)) returns 28.
*/
/*!
\fn uint qCountLeadingZeroBits(quint64 v)
\relates <QtAlgorithms>
\since 5.6
Returns the number of consecutive zero bits in \a v, when searching from the MSB.
For example, qCountLeadingZeroBits(quint64(1)) returns 63 and
qCountLeadingZeroBits(quint64(8)) returns 60.
*/

107
tests/auto/corelib/tools/qalgorithms/tst_qalgorithms.cpp Normal file → Executable file
View File

@ -31,6 +31,7 @@
**
****************************************************************************/
#include "../../../../../src/corelib/tools/qalgorithms.h"
#include <QtTest/QtTest>
#include <iostream>
@ -80,6 +81,24 @@ private slots:
void popCount32() { popCount_impl<quint32>(); }
void popCount64() { popCount_impl<quint64>(); }
void countTrailing08_data() { countTrailing_data_impl(sizeof(quint8 )); }
void countTrailing16_data() { countTrailing_data_impl(sizeof(quint16)); }
void countTrailing32_data() { countTrailing_data_impl(sizeof(quint32)); }
void countTrailing64_data() { countTrailing_data_impl(sizeof(quint64)); }
void countTrailing08() { countTrailing_impl<quint8 >(); }
void countTrailing16() { countTrailing_impl<quint16>(); }
void countTrailing32() { countTrailing_impl<quint32>(); }
void countTrailing64() { countTrailing_impl<quint64>(); }
void countLeading08_data() { countLeading_data_impl(sizeof(quint8 )); }
void countLeading16_data() { countLeading_data_impl(sizeof(quint16)); }
void countLeading32_data() { countLeading_data_impl(sizeof(quint32)); }
void countLeading64_data() { countLeading_data_impl(sizeof(quint64)); }
void countLeading08() { countLeading_impl<quint8 >(); }
void countLeading16() { countLeading_impl<quint16>(); }
void countLeading32() { countLeading_impl<quint32>(); }
void countLeading64() { countLeading_impl<quint64>(); }
private:
#if Q_TEST_PERFORMANCE
void performance();
@ -87,6 +106,14 @@ private:
void popCount_data_impl(size_t sizeof_T_Int);
template <typename T_Int>
void popCount_impl();
void countTrailing_data_impl(size_t sizeof_T_Int);
template <typename T_Int>
void countTrailing_impl();
void countLeading_data_impl(size_t sizeof_T_Int);
template <typename T_Int>
void countLeading_impl();
};
class TestInt
@ -1084,6 +1111,86 @@ void tst_QAlgorithms::popCount_impl()
QCOMPARE(qPopulationCount(value), expected);
}
void tst_QAlgorithms::countTrailing_data_impl(size_t sizeof_T_Int)
{
using namespace QTest;
addColumn<quint64>("input");
addColumn<uint>("expected");
int nibs = sizeof_T_Int*2;
newRow(("0x"+QByteArray::number(0,16).rightJustified(nibs,'0')).constData()) << Q_UINT64_C(0) << uint(sizeof_T_Int*8);
for (uint i = 0; i < sizeof_T_Int*8; ++i) {
const quint64 input = Q_UINT64_C(1) << i;
newRow(("0x"+QByteArray::number(input,16).rightJustified(nibs,'0')).constData()) << input << i;
}
quint64 type_mask;
if (sizeof_T_Int>=8)
type_mask = ~Q_UINT64_C(0);
else
type_mask = (Q_UINT64_C(1) << (sizeof_T_Int*8))-1;
// and some random ones:
for (uint i = 0; i < sizeof_T_Int*8; ++i) {
for (uint j = 0; j < sizeof_T_Int*3; ++j) { // 3 is arbitrary
const quint64 r = quint64(qrand()) << 32 | quint32(qrand());
const quint64 b = Q_UINT64_C(1) << i;
const quint64 mask = ((~(b-1)) ^ b) & type_mask;
const quint64 input = (r&mask) | b;
newRow(("0x"+QByteArray::number(input,16).rightJustified(nibs,'0')).constData()) << input << i;
}
}
}
template <typename T_Int>
void tst_QAlgorithms::countTrailing_impl()
{
QFETCH(quint64, input);
QFETCH(uint, expected);
const T_Int value = static_cast<T_Int>(input);
QCOMPARE(qCountTrailingZeroBits(value), expected);
}
void tst_QAlgorithms::countLeading_data_impl(size_t sizeof_T_Int)
{
using namespace QTest;
addColumn<quint64>("input");
addColumn<uint>("expected");
int nibs = sizeof_T_Int*2;
newRow(("0x"+QByteArray::number(0,16).rightJustified(nibs,'0')).constData()) << Q_UINT64_C(0) << uint(sizeof_T_Int*8);
for (uint i = 0; i < sizeof_T_Int*8; ++i) {
const quint64 input = Q_UINT64_C(1) << i;
newRow(("0x"+QByteArray::number(input,16).rightJustified(nibs,'0')).constData()) << input << uint(sizeof_T_Int*8-i-1);
}
// and some random ones:
for (uint i = 0; i < sizeof_T_Int*8; ++i) {
for (uint j = 0; j < sizeof_T_Int*3; ++j) { // 3 is arbitrary
const quint64 r = quint64(qrand()) << 32 | quint32(qrand());
const quint64 b = Q_UINT64_C(1) << i;
const quint64 mask = b-1;
const quint64 input = (r&mask) | b;
newRow(("0x"+QByteArray::number(input,16).rightJustified(nibs,'0')).constData()) << input << uint(sizeof_T_Int*8-i-1);
}
}
}
template <typename T_Int>
void tst_QAlgorithms::countLeading_impl()
{
QFETCH(quint64, input);
QFETCH(uint, expected);
const T_Int value = static_cast<T_Int>(input);
QCOMPARE(qCountLeadingZeroBits(value), expected);
}
QTEST_APPLESS_MAIN(tst_QAlgorithms)
#include "tst_qalgorithms.moc"