Add qPopulationCount() function, extracted from QBitArray
This functionality is used in multiple places in Qt itself, so it makes sense to have a global function for this. This also allows to map this onto specialized assembler instructions, should an architecture provide them, later on. Also added comprehensive tests, using a 4-bit lookup-table implementation as a reference. Change-Id: I8c4ea72cce54506ebb9fbe61141dbb5f1b7a660f Reviewed-by: Stephen Kelly <stephen.kelly@kdab.com>
This commit is contained in:
parent
6e7b8f79cb
commit
76e0223619
@ -516,6 +516,44 @@ Q_OUTOFLINE_TEMPLATE RandomAccessIterator qBinaryFindHelper(RandomAccessIterator
|
||||
|
||||
} //namespace QAlgorithmsPrivate
|
||||
|
||||
Q_DECL_CONSTEXPR inline uint qPopulationCount(quint32 v)
|
||||
{
|
||||
// See http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
|
||||
return
|
||||
(((v ) & 0xfff) * Q_UINT64_C(0x1001001001001) & Q_UINT64_C(0x84210842108421)) % 0x1f +
|
||||
(((v >> 12) & 0xfff) * Q_UINT64_C(0x1001001001001) & Q_UINT64_C(0x84210842108421)) % 0x1f +
|
||||
(((v >> 24) & 0xfff) * Q_UINT64_C(0x1001001001001) & Q_UINT64_C(0x84210842108421)) % 0x1f;
|
||||
}
|
||||
|
||||
Q_DECL_CONSTEXPR inline uint qPopulationCount(quint8 v)
|
||||
{
|
||||
return
|
||||
(((v ) & 0xfff) * Q_UINT64_C(0x1001001001001) & Q_UINT64_C(0x84210842108421)) % 0x1f;
|
||||
}
|
||||
|
||||
Q_DECL_CONSTEXPR inline uint qPopulationCount(quint16 v)
|
||||
{
|
||||
return
|
||||
(((v ) & 0xfff) * Q_UINT64_C(0x1001001001001) & Q_UINT64_C(0x84210842108421)) % 0x1f +
|
||||
(((v >> 12) & 0xfff) * Q_UINT64_C(0x1001001001001) & Q_UINT64_C(0x84210842108421)) % 0x1f;
|
||||
}
|
||||
|
||||
Q_DECL_CONSTEXPR inline uint qPopulationCount(quint64 v)
|
||||
{
|
||||
return
|
||||
(((v ) & 0xfff) * Q_UINT64_C(0x1001001001001) & Q_UINT64_C(0x84210842108421)) % 0x1f +
|
||||
(((v >> 12) & 0xfff) * Q_UINT64_C(0x1001001001001) & Q_UINT64_C(0x84210842108421)) % 0x1f +
|
||||
(((v >> 24) & 0xfff) * Q_UINT64_C(0x1001001001001) & Q_UINT64_C(0x84210842108421)) % 0x1f +
|
||||
(((v >> 36) & 0xfff) * Q_UINT64_C(0x1001001001001) & Q_UINT64_C(0x84210842108421)) % 0x1f +
|
||||
(((v >> 48) & 0xfff) * Q_UINT64_C(0x1001001001001) & Q_UINT64_C(0x84210842108421)) % 0x1f +
|
||||
(((v >> 60) & 0xfff) * Q_UINT64_C(0x1001001001001) & Q_UINT64_C(0x84210842108421)) % 0x1f;
|
||||
}
|
||||
|
||||
Q_DECL_CONSTEXPR inline uint qPopulationCount(long unsigned int v)
|
||||
{
|
||||
return qPopulationCount(static_cast<quint64>(v));
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QALGORITHMS_H
|
||||
|
@ -635,3 +635,34 @@ of \a value in the variable passed as a reference in argument \a n.
|
||||
|
||||
\sa {qLess()}{qLess<T>()}
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\fn uint qPopulationCount(quint8 v)
|
||||
\relates <QtAlgorithms>
|
||||
\since 5.2
|
||||
|
||||
Returns the number of bits set in \a v. This number also called
|
||||
the Hamming Weight of \a v.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn uint qPopulationCount(quint16 v)
|
||||
\relates <QtAlgorithms>
|
||||
\since 5.2
|
||||
\overload
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn uint qPopulationCount(quint32 v)
|
||||
\relates <QtAlgorithms>
|
||||
\since 5.2
|
||||
\overload
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn uint qPopulationCount(quint64 v)
|
||||
\relates <QtAlgorithms>
|
||||
\since 5.2
|
||||
\overload
|
||||
*/
|
||||
|
@ -40,6 +40,7 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "qbitarray.h"
|
||||
#include <qalgorithms.h>
|
||||
#include <qdatastream.h>
|
||||
#include <qdebug.h>
|
||||
#include <string.h>
|
||||
@ -159,24 +160,18 @@ int QBitArray::count(bool on) const
|
||||
for (int i = 0; i < len; ++i)
|
||||
numBits += testBit(i);
|
||||
#else
|
||||
// See http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
|
||||
const quint8 *bits = reinterpret_cast<const quint8 *>(d.data()) + 1;
|
||||
while (len >= 32) {
|
||||
quint32 v = quint32(bits[0]) | (quint32(bits[1]) << 8) | (quint32(bits[2]) << 16) | (quint32(bits[3]) << 24);
|
||||
quint32 c = ((v & 0xfff) * Q_UINT64_C(0x1001001001001) & Q_UINT64_C(0x84210842108421)) % 0x1f;
|
||||
c += (((v & 0xfff000) >> 12) * Q_UINT64_C(0x1001001001001) & Q_UINT64_C(0x84210842108421)) % 0x1f;
|
||||
c += ((v >> 24) * Q_UINT64_C(0x1001001001001) & Q_UINT64_C(0x84210842108421)) % 0x1f;
|
||||
len -= 32;
|
||||
bits += 4;
|
||||
numBits += int(c);
|
||||
numBits += int(qPopulationCount(v));
|
||||
}
|
||||
while (len >= 24) {
|
||||
quint32 v = quint32(bits[0]) | (quint32(bits[1]) << 8) | (quint32(bits[2]) << 16);
|
||||
quint32 c = ((v & 0xfff) * Q_UINT64_C(0x1001001001001) & Q_UINT64_C(0x84210842108421)) % 0x1f;
|
||||
c += (((v & 0xfff000) >> 12) * Q_UINT64_C(0x1001001001001) & Q_UINT64_C(0x84210842108421)) % 0x1f;
|
||||
len -= 24;
|
||||
bits += 3;
|
||||
numBits += int(c);
|
||||
numBits += int(qPopulationCount(v));
|
||||
}
|
||||
while (len >= 0) {
|
||||
if (bits[len / 8] & (1 << ((len - 1) & 7)))
|
||||
|
@ -41,17 +41,6 @@
|
||||
|
||||
#include "qxlibeglintegration_p.h"
|
||||
|
||||
static int countBits(unsigned long mask)
|
||||
{
|
||||
int count = 0;
|
||||
while (mask != 0) {
|
||||
if (mask & 1)
|
||||
++count;
|
||||
mask >>= 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
VisualID QXlibEglIntegration::getCompatibleVisualId(Display *display, EGLDisplay eglDisplay, EGLConfig config)
|
||||
{
|
||||
VisualID visualId = 0;
|
||||
@ -92,9 +81,9 @@ VisualID QXlibEglIntegration::getCompatibleVisualId(Display *display, EGLDisplay
|
||||
return visualId;
|
||||
}
|
||||
|
||||
int visualRedSize = countBits(chosenVisualInfo->red_mask);
|
||||
int visualGreenSize = countBits(chosenVisualInfo->green_mask);
|
||||
int visualBlueSize = countBits(chosenVisualInfo->blue_mask);
|
||||
int visualRedSize = qPopulationCount(chosenVisualInfo->red_mask);
|
||||
int visualGreenSize = qPopulationCount(chosenVisualInfo->green_mask);
|
||||
int visualBlueSize = qPopulationCount(chosenVisualInfo->blue_mask);
|
||||
int visualAlphaSize = -1; // Need XRender to tell us the alpha channel size
|
||||
|
||||
bool visualMatchesConfig = false;
|
||||
|
@ -78,10 +78,22 @@ private slots:
|
||||
void qCountContainer() const;
|
||||
void binaryFindOnLargeContainer() const;
|
||||
|
||||
#if Q_TEST_PERFORMANCE
|
||||
void popCount08_data() { popCount_data_impl(sizeof(quint8 )); }
|
||||
void popCount16_data() { popCount_data_impl(sizeof(quint16)); }
|
||||
void popCount32_data() { popCount_data_impl(sizeof(quint32)); }
|
||||
void popCount64_data() { popCount_data_impl(sizeof(quint64)); }
|
||||
void popCount08() { popCount_impl<quint8 >(); }
|
||||
void popCount16() { popCount_impl<quint16>(); }
|
||||
void popCount32() { popCount_impl<quint32>(); }
|
||||
void popCount64() { popCount_impl<quint64>(); }
|
||||
|
||||
private:
|
||||
#if Q_TEST_PERFORMANCE
|
||||
void performance();
|
||||
#endif
|
||||
void popCount_data_impl(size_t sizeof_T_Int);
|
||||
template <typename T_Int>
|
||||
void popCount_impl();
|
||||
};
|
||||
|
||||
class TestInt
|
||||
@ -1007,6 +1019,72 @@ void tst_QAlgorithms::binaryFindOnLargeContainer() const
|
||||
QCOMPARE(foundIt.pos(), 1073987655);
|
||||
}
|
||||
|
||||
// alternative implementation of qPopulationCount for comparison:
|
||||
static const uint bitsSetInNibble[] = {
|
||||
0, 1, 1, 2, 1, 2, 2, 3,
|
||||
1, 2, 2, 3, 2, 3, 3, 4,
|
||||
};
|
||||
Q_STATIC_ASSERT(sizeof bitsSetInNibble / sizeof *bitsSetInNibble == 16);
|
||||
|
||||
static Q_DECL_CONSTEXPR uint bitsSetInByte(quint8 byte)
|
||||
{
|
||||
return bitsSetInNibble[byte & 0xF] + bitsSetInNibble[byte >> 4];
|
||||
}
|
||||
static Q_DECL_CONSTEXPR uint bitsSetInShort(quint16 word)
|
||||
{
|
||||
return bitsSetInByte(word & 0xFF) + bitsSetInByte(word >> 8);
|
||||
}
|
||||
static Q_DECL_CONSTEXPR uint bitsSetInInt(quint32 word)
|
||||
{
|
||||
return bitsSetInShort(word & 0xFFFF) + bitsSetInShort(word >> 16);
|
||||
}
|
||||
static Q_DECL_CONSTEXPR uint bitsSetInInt64(quint64 word)
|
||||
{
|
||||
return bitsSetInInt(word & 0xFFFFFFFF) + bitsSetInInt(word >> 32);
|
||||
}
|
||||
|
||||
|
||||
void tst_QAlgorithms::popCount_data_impl(size_t sizeof_T_Int)
|
||||
{
|
||||
using namespace QTest;
|
||||
addColumn<quint64>("input");
|
||||
addColumn<uint>("expected");
|
||||
|
||||
for (uint i = 0; i < UCHAR_MAX; ++i) {
|
||||
const uchar byte = static_cast<uchar>(i);
|
||||
const uint bits = bitsSetInByte(byte);
|
||||
const quint64 value = static_cast<quint64>(byte);
|
||||
const quint64 input = value << ((i % sizeof_T_Int) * 8U);
|
||||
newRow(qPrintable(QString().sprintf("0x%016llx", input))) << input << bits;
|
||||
}
|
||||
|
||||
// and some random ones:
|
||||
if (sizeof_T_Int >= 8)
|
||||
for (size_t i = 0; i < 1000; ++i) {
|
||||
const quint64 input = quint64(qrand()) << 32 | quint32(qrand());
|
||||
newRow(qPrintable(QString().sprintf("0x%016llx", input))) << input << bitsSetInInt64(input);
|
||||
}
|
||||
else if (sizeof_T_Int >= 2)
|
||||
for (size_t i = 0; i < 1000 ; ++i) {
|
||||
const quint32 input = qrand();
|
||||
if (sizeof_T_Int >= 4)
|
||||
newRow(qPrintable(QString().sprintf("0x%08x", input))) << quint64(input) << bitsSetInInt(input);
|
||||
else
|
||||
newRow(qPrintable(QString().sprintf("0x%04x", quint16(input & 0xFFFF)))) << quint64(input & 0xFFFF) << bitsSetInShort(input & 0xFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T_Int>
|
||||
void tst_QAlgorithms::popCount_impl()
|
||||
{
|
||||
QFETCH(quint64, input);
|
||||
QFETCH(uint, expected);
|
||||
|
||||
const T_Int value = static_cast<T_Int>(input);
|
||||
|
||||
QCOMPARE(qPopulationCount(value), expected);
|
||||
}
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_QAlgorithms)
|
||||
#include "tst_qalgorithms.moc"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user