diff --git a/src/Makefile.am b/src/Makefile.am --- a/src/Makefile.am +++ b/src/Makefile.am @@ -207,6 +207,7 @@ util.h \ utilmoneystr.h \ utiltime.h \ + util/bitmanip.h \ util/bytevectorhash.h \ validation.h \ validationinterface.h \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -42,6 +42,7 @@ test/base58_tests.cpp \ test/base64_tests.cpp \ test/bip32_tests.cpp \ + test/bitmanip_tests.cpp \ test/blockchain_tests.cpp \ test/blockcheck_tests.cpp \ test/blockencodings_tests.cpp \ diff --git a/src/avalanche.cpp b/src/avalanche.cpp --- a/src/avalanche.cpp +++ b/src/avalanche.cpp @@ -5,10 +5,10 @@ #include #include -#include #include #include #include +#include #include #include @@ -23,23 +23,6 @@ */ static const size_t AVALANCHE_MAX_ELEMENT_POLL = 4096; -static uint32_t countBits(uint32_t v) { -#if HAVE_DECL___BUILTIN_POPCOUNT - return __builtin_popcount(v); -#else - /** - * Computes the number of bits set in each group of 8bits then uses a - * multiplication to sum all of them in the 8 most significant bits and - * return these. - * More detailed explanation can be found at - * https://www.playingwithpointers.com/blog/swar.html - */ - v = v - ((v >> 1) & 0x55555555); - v = (v & 0x33333333) + ((v >> 2) & 0x33333333); - return (((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; -#endif -} - bool VoteRecord::registerVote(NodeId nodeid, uint32_t error) { // We just got a new vote, so there is one less inflight request. clearInflightRequest(); diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -55,6 +55,7 @@ base58_tests.cpp base64_tests.cpp bip32_tests.cpp + bitmanip_tests.cpp blockchain_tests.cpp blockcheck_tests.cpp blockencodings_tests.cpp diff --git a/src/test/bitmanip_tests.cpp b/src/test/bitmanip_tests.cpp new file mode 100644 --- /dev/null +++ b/src/test/bitmanip_tests.cpp @@ -0,0 +1,60 @@ +// Copyright (c) 2019 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +#include + +BOOST_FIXTURE_TEST_SUITE(bitmanip_tests, BasicTestingSetup) + +static void CheckBitCount(uint32_t value, uint32_t expected_count) { + // Count are rotation invariant. + for (int i = 0; i < 32; i++) { + BOOST_CHECK_EQUAL(countBits(value), expected_count); + value = (value << 1) | (value >> 31); + } +} + +static uint32_t countBitsNaive(uint32_t value) { + uint32_t ret = 0; + while (value != 0) { + ret += (value & 0x01); + value >>= 1; + } + + return ret; +} + +#define COUNT 4096 + +BOOST_AUTO_TEST_CASE(bit_count) { + // Check various known values. + CheckBitCount(0, 0); + CheckBitCount(1, 1); + CheckBitCount(0xffffffff, 32); + CheckBitCount(0x01234567, 12); + CheckBitCount(0x12345678, 13); + CheckBitCount(0xfedcba98, 20); + CheckBitCount(0x5a55aaa5, 16); + CheckBitCount(0xdeadbeef, 24); + + for (uint32_t i = 2; i != 0; i <<= 1) { + // Check two bit set for all combinations. + CheckBitCount(i | 0x01, 2); + } + + // Check many small values against a naive implementation. + for (uint32_t v = 0; v <= 0xfff; v++) { + CheckBitCount(v, countBitsNaive(v)); + } + + // Check random values against a naive implementation. + for (int i = 0; i < COUNT; i++) { + uint32_t v = InsecureRand32(); + CheckBitCount(v, countBitsNaive(v)); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/util/bitmanip.h b/src/util/bitmanip.h new file mode 100644 --- /dev/null +++ b/src/util/bitmanip.h @@ -0,0 +1,29 @@ +// Copyright (c) 2019 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_UTIL_BITMANIP_H +#define BITCOIN_UTIL_BITMANIP_H + +#include + +#include + +inline uint32_t countBits(uint32_t v) { +#if HAVE_DECL___BUILTIN_POPCOUNT + return __builtin_popcount(v); +#else + /** + * Computes the number of bits set in each group of 8bits then uses a + * multiplication to sum all of them in the 8 most significant bits and + * return these. + * More detailed explanation can be found at + * https://www.playingwithpointers.com/blog/swar.html + */ + v = v - ((v >> 1) & 0x55555555); + v = (v & 0x33333333) + ((v >> 2) & 0x33333333); + return (((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; +#endif +} + +#endif // BITCOIN_UTIL_BITMANIP_H