diff --git a/src/bitarray.cpp b/src/bitarray.cpp index f20ca97..695265b 100644 --- a/src/bitarray.cpp +++ b/src/bitarray.cpp @@ -472,13 +472,12 @@ namespace bx } template - inline uint32_t bitSetFindT(const uint64_t* _ptr, uint32_t _beginBit, uint32_t _endBit, uint32_t _numSteps) + inline uint32_t bitSetFindT(const uint64_t* _ptr, uint32_t _beginBit, uint32_t _endBit, uint32_t _numBits) { const Ty* bits = (const Ty*)_ptr; constexpr uint32_t numBitsPerStep = sizeof(Ty)*8; - const uint32_t numBits = _numSteps*numBitsPerStep; - for (uint32_t ii = _beginBit/numBitsPerStep, end = alignUp(_endBit, numBitsPerStep)/numBitsPerStep; ii < end; ++ii) + for (uint32_t ii = alignUp(_beginBit, numBitsPerStep)/numBitsPerStep, end = alignUp(_endBit, numBitsPerStep)/numBitsPerStep; ii < end; ++ii) { if (bits[ii] != Ty(0) ) { @@ -499,7 +498,7 @@ namespace bx for (++ii; ii < end; ++ii) { - if (num >= numBits) + if (num >= _numBits) { return idx; } @@ -514,7 +513,7 @@ namespace bx } } - if (num >= numBits) + if (num >= _numBits) { return idx; } @@ -538,6 +537,7 @@ namespace bx if (0xff == test) { + lz = 0; continue; } @@ -553,7 +553,8 @@ namespace bx const uint32_t idx = pos + 8; const uint32_t newIdx = max(idx - lz, _beginBit); - lz = min(idx, _endBit) - newIdx; + const uint32_t endIdx = min(idx, _endBit); + lz = endIdx > newIdx ? endIdx - newIdx : 0; if (lz >= _numBits) { @@ -629,70 +630,39 @@ namespace bx return bitSetFindZero(_ptr, _beginBit, _endBit); } + // Run of 2*N-1 or more zero bits always contains whole N-bit aligned zero word. + uint32_t idx; + if (_numBits >= 127) { - const uint32_t idx = bitSetFindT(_ptr, _beginBit, _endBit, (_numBits+63)>>6); - if (idx != _endBit) + idx = bitSetFindT(_ptr, _beginBit, _endBit, _numBits); + } + else if (_numBits >= 63) + { + idx = bitSetFindT(_ptr, _beginBit, _endBit, _numBits); + } + else if (_numBits >= 31) + { + idx = bitSetFindT(_ptr, _beginBit, _endBit, _numBits); + } + else + { + idx = bitSetFindT(_ptr, _beginBit, _endBit, _numBits); + + if (idx == _endBit + && _numBits < 15) { - return idx; - } - } + idx = bitSetFindUpto14(_ptr, _beginBit, _endBit, _numBits); - if (_numBits >= 63) - { - const uint32_t idx = bitSetFindT(_ptr, _beginBit, _endBit, (_numBits+31)>>5); - if (idx != _endBit) - { - return idx; - } - } - - if (_numBits >= 128) - { - return kInvalid; - } - - if (_numBits >= 31) - { - const uint32_t idx = bitSetFindT(_ptr, _beginBit, _endBit, (_numBits+15)>>4); - if (idx != _endBit) - { - return idx; - } - } - - if (_numBits >= 64) - { - return kInvalid; - } - - uint32_t idx = bitSetFindT(_ptr, _beginBit, _endBit, (_numBits+7)>>3); - if (idx != _endBit) - { - return idx; - } - - if (_numBits < 15) - { - idx = bitSetFindUpto14(_ptr, _beginBit, _endBit, _numBits); - - if (idx != _endBit) - { - return idx; - } - - if (_numBits < 7) - { - idx = bitSetFindUpto6(_ptr, _beginBit, _endBit, _numBits); - - if (idx != _endBit) + if (idx == _endBit + && _numBits < 7) { - return idx; + idx = bitSetFindUpto6(_ptr, _beginBit, _endBit, _numBits); } } } - return kInvalid; + return idx != _endBit ? idx : kInvalid; } uint64_t BitArrayViewBase::get(uint32_t _beginBit, uint32_t _endBit) const diff --git a/tests/bitarray_test.cpp b/tests/bitarray_test.cpp index 4ff7ee6..2bde8d2 100644 --- a/tests/bitarray_test.cpp +++ b/tests/bitarray_test.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include static void testRange(bx::MutableBitArrayView& _bs, uint32_t _beginBit, uint32_t _endBit) { @@ -976,3 +978,112 @@ TEST_CASE("BitArray-ranges-ending-on-word-boundary", "[container]") REQUIRE( (UINT64_MAX >> 36) == (words[1] >> 36) ); REQUIRE( (UINT64_MAX >> 28) == words[2]); } + +static uint32_t findRef(const bx::MutableBitArrayView& _bs, uint32_t _beginBit, uint32_t _endBit, uint32_t _numBits) +{ + for (uint32_t ii = _beginBit; ii + _numBits <= _endBit; ++ii) + { + if (_bs.testNone(ii, ii + _numBits) ) + { + return ii; + } + } + + return bx::kInvalid; +} + +TEST_CASE("BitArray-find-random", "[container]") +{ + constexpr uint32_t kNumBits = 1024; + + uint64_t bits[kNumBits/64]; + bx::MutableBitArrayView bs(bits, sizeof(bits) ); + + bx::RngMwc rng; + + for (uint32_t iter = 0; iter < 20000; ++iter) + { + const uint32_t density = rng.gen() % 4; + + bs.clear(); + + for (uint32_t ii = 0, num = 4 + rng.gen() % 64; ii < num; ++ii) + { + const uint32_t begin = rng.gen() % kNumBits; + const uint32_t end = bx::min(kNumBits, begin + 1 + rng.gen() % (8 << density) ); + bs.set(begin, end, true); + } + + for (uint32_t ii = 0, num = rng.gen() % 32; ii < num; ++ii) + { + const uint32_t begin = rng.gen() % kNumBits; + const uint32_t end = bx::min(kNumBits, begin + 1 + rng.gen() % 64); + bs.set(begin, end, false); + } + + const uint32_t numBits = 1 + rng.gen() % 160; + const bool full = 0 == (rng.gen() & 3); + const uint32_t beginBit = full ? 0 : rng.gen() % (kNumBits - numBits); + const uint32_t endBit = full ? kNumBits : beginBit + numBits + rng.gen() % (kNumBits - beginBit - numBits + 1); + + const uint32_t expected = findRef(bs, beginBit, endBit, numBits); + const uint32_t idx = full ? bs.find(numBits) : bs.find(beginBit, endBit, numBits); + + INFO("Iteration " << iter << ", range [" << beginBit << ", " << endBit << "), num bits " << numBits << ", found " << idx << ", expected " << expected); + + REQUIRE( (bx::kInvalid == idx) == (bx::kInvalid == expected) ); + + if (bx::kInvalid != idx) + { + REQUIRE(beginBit <= idx); + REQUIRE(idx + numBits <= endBit); + REQUIRE(bs.testNone(idx, idx + numBits) ); + } + } +} + +TEST_CASE("BitArray-find-skips-full-byte", "[container]") +{ + uint64_t bits[1]; + bx::MutableBitArrayView bs(bits, sizeof(bits) ); + + bs.set(0, 64, true); + bs.set(5, 8, false); + bs.set(16, 23, false); + + REQUIRE(bx::kInvalid == bs.find(10) ); + REQUIRE(16 == bs.find(7) ); +} + +TEST_CASE("BitArray-find-bench", "[.][!benchmark]") +{ + constexpr uint32_t kNumBits = 4096; + + uint64_t bits[kNumBits/64]; + bx::MutableBitArrayView bs(bits, sizeof(bits) ); + + bx::RngMwc rng; + + for (uint32_t ii = 0; ii < kNumBits; ii += 1 + rng.gen() % 12) + { + bs.set(ii, true); + } + + bs.set(kNumBits - 256, kNumBits, false); + + char name[64]; + + for (uint32_t numBits : { 4u, 12u, 40u, 200u, 300u }) + { + uint32_t idx = 0; + + bx::snprintf(name, sizeof(name), "find %u", numBits); + BENCHMARK(name) + { + idx = bs.find(numBits); + return idx; + }; + + REQUIRE( (bx::kInvalid == idx) == (bx::kInvalid == findRef(bs, 0, kNumBits, numBits) ) ); + } +}