/* * Copyright (c) 2015-2017, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Intel Corporation nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "gtest/gtest.h" #include "ue2common.h" #include "rose/rose_build_scatter.h" #include "util/compile_error.h" #include "util/multibit.h" #include "util/multibit_build.h" #include #include #include using namespace std; using namespace testing; using namespace ue2; namespace { class mmbit_holder { public: mmbit_holder() {} explicit mmbit_holder(u32 num_bits, u32 excess = 0) : data(std::make_unique(mmbit_size(num_bits) + 7 + excess)) {} void init(u32 num_bits) { assert(!data); data = std::make_unique(mmbit_size(num_bits) + 7); } operator u8 *() { assert(data); return data.get() + 7; } operator const u8 *() const { assert(data); return data.get() + 7; } private: unique_ptr data = nullptr; }; } static void fill_mmbit(u8 *ba, u32 test_size) { fill_n(ba, mmbit_size(test_size), 0xff); } TEST(MultiBit, Set1) { const u32 test_size = 32; mmbit_holder ba(test_size); fill_mmbit(ba, test_size); mmbit_clear(ba, test_size); ASSERT_FALSE(mmbit_any(ba, 32)); ASSERT_FALSE(mmbit_isset(ba, 32, 0)); mmbit_set(ba, 32, 0); ASSERT_TRUE(mmbit_isset(ba, 32, 0)); ASSERT_TRUE(mmbit_any(ba, 32)); ASSERT_FALSE(mmbit_isset(ba, 32, 31)); mmbit_set(ba, 32, 31); ASSERT_TRUE(mmbit_isset(ba, 32, 31)); } TEST(MultiBit, Set2) { const u32 test_size = 64; mmbit_holder ba(test_size); fill_mmbit(ba, test_size); mmbit_clear(ba, test_size); ASSERT_FALSE(mmbit_any(ba, 64)); for (u32 i = 0; i < 64; i++) { ASSERT_FALSE(mmbit_isset(ba, 64, i)); } ASSERT_FALSE(mmbit_isset(ba, 64, 0)); mmbit_set(ba, 64, 0); ASSERT_TRUE(mmbit_isset(ba, 64, 0)); for (u32 i = 1; i < 64; i++) { ASSERT_FALSE(mmbit_isset(ba, 64, i)); } ASSERT_TRUE(mmbit_any(ba, 64)); ASSERT_FALSE(mmbit_isset(ba, 64, 31)); mmbit_set(ba, 64, 31); ASSERT_TRUE(mmbit_isset(ba, 64, 31)); ASSERT_FALSE(mmbit_isset(ba, 64, 32)); mmbit_set(ba, 64, 32); ASSERT_TRUE(mmbit_isset(ba, 64, 32)); ASSERT_FALSE(mmbit_isset(ba, 64, 48)); mmbit_set(ba, 64, 48); ASSERT_TRUE(mmbit_isset(ba, 64, 48)); } TEST(MultiBit, Set3) { const u32 test_size = 1030; mmbit_holder ba(test_size); fill_mmbit(ba, test_size); mmbit_clear(ba, test_size); ASSERT_FALSE(mmbit_any(ba, test_size)); ASSERT_FALSE(mmbit_isset(ba, test_size, 0)); mmbit_set(ba, test_size, 0); ASSERT_TRUE(mmbit_isset(ba, test_size, 0)); ASSERT_TRUE(mmbit_any(ba, test_size)); ASSERT_FALSE(mmbit_isset(ba, test_size, 31)); mmbit_set(ba, test_size, 31); ASSERT_TRUE(mmbit_isset(ba, test_size, 31)); ASSERT_FALSE(mmbit_isset(ba, test_size, 32)); mmbit_set(ba, test_size, 32); ASSERT_TRUE(mmbit_isset(ba, test_size, 32)); ASSERT_FALSE(mmbit_isset(ba, test_size, 48)); mmbit_set(ba, test_size, 48); ASSERT_TRUE(mmbit_isset(ba, test_size, 48)); ASSERT_FALSE(mmbit_isset(ba, test_size, 60)); mmbit_set(ba, test_size, 60); ASSERT_TRUE(mmbit_isset(ba, test_size, 60)); ASSERT_FALSE(mmbit_isset(ba, test_size, 1029)); mmbit_set(ba, test_size, 1029); ASSERT_TRUE(mmbit_isset(ba, test_size, 1029)); } TEST(MultiBit, Set4) { const u32 test_size = 1025; mmbit_holder ba(test_size); mmbit_clear(ba, test_size); ASSERT_FALSE(mmbit_any(ba, test_size)); for (u32 i = 0; i < test_size; i++) { ASSERT_FALSE(mmbit_isset(ba, test_size, i)); mmbit_set(ba, test_size, i); ASSERT_TRUE(mmbit_isset(ba, test_size, i)); ASSERT_EQ(i, mmbit_iterate(ba, test_size, MMB_INVALID)); ASSERT_EQ(MMB_INVALID, mmbit_iterate(ba, test_size, i)); mmbit_unset(ba, test_size, i); ASSERT_FALSE(mmbit_isset(ba, test_size, i)); } } TEST(MultiBit, Set5) { const u32 test_size = 33000; mmbit_holder ba(test_size); mmbit_clear(ba, test_size); ASSERT_FALSE(mmbit_any(ba, test_size)); for (u32 i = 0; i < test_size; i += 8) { ASSERT_FALSE(mmbit_isset(ba, test_size, i)); mmbit_set(ba, test_size, i); ASSERT_TRUE(mmbit_isset(ba, test_size, i)); ASSERT_EQ(i, mmbit_iterate(ba, test_size, MMB_INVALID)); ASSERT_EQ(MMB_INVALID, mmbit_iterate(ba, test_size, i)); mmbit_unset(ba, test_size, i); ASSERT_FALSE(mmbit_isset(ba, test_size, i)); } } TEST(MultiBit, Set8) { const u32 test_size = 8; mmbit_holder ba(test_size); mmbit_clear(ba, test_size); ASSERT_FALSE(mmbit_any(ba, test_size)); for (u32 i = 0; i < test_size; i++) { ASSERT_FALSE(mmbit_isset(ba, test_size, i)); mmbit_set(ba, test_size, i); ASSERT_TRUE(mmbit_isset(ba, test_size, i)); ASSERT_EQ(i, mmbit_iterate(ba, test_size, MMB_INVALID)); ASSERT_EQ(MMB_INVALID, mmbit_iterate(ba, test_size, i)); mmbit_unset(ba, test_size, i); ASSERT_FALSE(mmbit_isset(ba, test_size, i)); } } TEST(MultiBit, Set16) { const u32 test_size = 16; mmbit_holder ba(test_size); mmbit_clear(ba, test_size); ASSERT_FALSE(mmbit_any(ba, test_size)); for (u32 i = 0; i < test_size; i++) { ASSERT_FALSE(mmbit_isset(ba, test_size, i)); mmbit_set(ba, test_size, i); ASSERT_TRUE(mmbit_isset(ba, test_size, i)); ASSERT_EQ(i, mmbit_iterate(ba, test_size, MMB_INVALID)); ASSERT_EQ(MMB_INVALID, mmbit_iterate(ba, test_size, i)); mmbit_unset(ba, test_size, i); ASSERT_FALSE(mmbit_isset(ba, test_size, i)); } } TEST(MultiBit, It1) { const u32 test_size = 32; mmbit_holder ba(test_size); mmbit_clear(ba, test_size); ASSERT_EQ(MMB_INVALID, mmbit_iterate(ba, test_size, MMB_INVALID)); for (u32 i = 0; i < test_size; i+=3) { mmbit_set(ba, test_size, i); } ASSERT_EQ(0U, mmbit_iterate(ba, test_size, MMB_INVALID)); for (u32 i = 0; i < 29; i++) { ASSERT_EQ(i/3 * 3 + 3, mmbit_iterate(ba, test_size, i)); } for (u32 i = 0; i < test_size; i++) { mmbit_set(ba, test_size, i); } ASSERT_EQ(0U, mmbit_iterate(ba, test_size, MMB_INVALID)); for (u32 i = 1; i < test_size; i++) { ASSERT_EQ(i, mmbit_iterate(ba, test_size, i - 1)); } } TEST(MultiBit, It2) { mmbit_holder ba(64); mmbit_clear(ba, 64); ASSERT_EQ(MMB_INVALID, mmbit_iterate(ba, 64, MMB_INVALID)); for (u32 i = 0; i < 64; i+=3) { mmbit_set(ba, 64, i); } ASSERT_EQ(0U, mmbit_iterate(ba, 64, MMB_INVALID)); for (u32 i = 0; i < 62; i++) { ASSERT_EQ(i/3 * 3 + 3, mmbit_iterate(ba, 64, i)); } for (u32 i = 0; i < 64; i++) { mmbit_set(ba, 64, i); } ASSERT_EQ(0U, mmbit_iterate(ba, 64, MMB_INVALID)); for (u32 i = 1; i < 64; i++) { ASSERT_EQ(i, mmbit_iterate(ba, 64, i - 1)); } } TEST(MultiBit, It3) { const size_t test_size = 60; mmbit_holder ba(test_size, 4); fill_n((u8 *)ba, mmbit_size(test_size) + 4, 0xff); mmbit_clear(ba, test_size); ASSERT_EQ(MMB_INVALID, mmbit_iterate(ba, test_size, MMB_INVALID)); for (u32 i = 0; i < test_size; i+=3) { mmbit_set(ba, test_size, i); } ASSERT_EQ(0U, mmbit_iterate(ba, test_size, MMB_INVALID)); for (u32 i = 0; i < test_size - 3; i++) { ASSERT_EQ(i/3 * 3 + 3, mmbit_iterate(ba, test_size, i)); } for (u32 i = 0; i < test_size; i++) { mmbit_set(ba, test_size, i); } ASSERT_EQ(0U, mmbit_iterate(ba, test_size, MMB_INVALID)); for (u32 i = 1; i < test_size; i++) { ASSERT_EQ(i, mmbit_iterate(ba, test_size, i - 1)); } } // We provide both test size and stride so that larger tests don't take forever // checking every single key. struct MultiBitTestParam { u32 size; u32 stride; }; // Parameterized test case for bounded iterator, rather that propagating // copypasta. Allocates space as given. class MultiBitTest : public TestWithParam { protected: virtual void SetUp() { const MultiBitTestParam &p = GetParam(); test_size = p.size; stride = p.stride; ba.init(test_size); // blast with ones for the lulz fill_mmbit(ba, test_size); } virtual void TearDown() { } u32 test_size; // number of bits in the multibit u32 stride; // stride to use for scans mmbit_holder ba; // multibit storage }; TEST_P(MultiBitTest, BoundedIteratorSingle) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); // Set one bit on and run some checks. for (u64a i = 0; i < test_size; i += stride) { SCOPED_TRACE(i); mmbit_clear(ba, test_size); mmbit_set(ba, test_size, i); // Scanning from the start to our bit should find nothing. ASSERT_EQ(MMB_INVALID, mmbit_iterate_bounded(ba, test_size, i, i)); // Scanning from the start to the end should find our bit. ASSERT_EQ(i, mmbit_iterate_bounded(ba, test_size, 0, test_size)); // Scanning from our bit to one past it should find itself. ASSERT_EQ(i, mmbit_iterate_bounded(ba, test_size, i, i + 1)); // Scanning from our bit to the end should find itself. ASSERT_EQ(i, mmbit_iterate_bounded(ba, test_size, i, test_size)); // Scanning from one past our bit to the end should find nothing. if (i != test_size - 1) { // Ordinary iterator. ASSERT_EQ(MMB_INVALID, mmbit_iterate(ba, test_size, i)); // Bounded iterator. ASSERT_EQ(MMB_INVALID, mmbit_iterate_bounded(ba, test_size, i + 1, test_size)); } } } TEST_P(MultiBitTest, BoundedIteratorAll) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); // Switch everything on. fill_mmbit(ba, test_size); for (u64a i = 0; i < test_size; i += stride) { if (i != 0) { ASSERT_EQ(0U, mmbit_iterate_bounded(ba, test_size, 0, i)); } ASSERT_EQ(i, mmbit_iterate_bounded(ba, test_size, i, i+1)); ASSERT_EQ(i, mmbit_iterate_bounded(ba, test_size, i, test_size)); } } TEST_P(MultiBitTest, BoundedIteratorEven) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); // Set every even-numbered bit and see what we can see. mmbit_clear(ba, test_size); for (u64a i = 0; i < test_size; i += 2) { mmbit_set(ba, test_size, i); } u32 even_stride = stride % 2 ? stride + 1 : stride; for (u64a i = 0; i < test_size; i += even_stride) { // Scanning from each even bit to the end should find itself. ASSERT_EQ(i, mmbit_iterate_bounded(ba, test_size, i, test_size)); // Scanning from i to i+1 should find itself. ASSERT_EQ(i, mmbit_iterate_bounded(ba, test_size, i, i+1)); // Scanning from i+1 to i+2 should find nothing. if (i+1 < test_size) { ASSERT_EQ(MMB_INVALID, mmbit_iterate_bounded(ba, test_size, i+1, i+2)); } // Scanning from i+1 to the end should find i+2. if (i+2 < test_size) { ASSERT_EQ(i+2, mmbit_iterate_bounded(ba, test_size, i+1, test_size)); } } } TEST_P(MultiBitTest, BoundedIteratorOdd) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); // Set every odd-numbered bit and see what we can see. mmbit_clear(ba, test_size); for (u64a i = 1; i < test_size; i += 2) { mmbit_set(ba, test_size, i); } u32 even_stride = stride % 2 ? stride + 1 : stride; for (u64a i = 0; i < test_size; i += even_stride) { // Scanning from each even bit to the end should find i+1. if (i+1 < test_size) { ASSERT_EQ(i+1, mmbit_iterate_bounded(ba, test_size, i, test_size)); } // Scanning from i to i+1 should find nothing. ASSERT_EQ(MMB_INVALID, mmbit_iterate_bounded(ba, test_size, i, i+1)); // Scanning from i+1 to i+2 should find i+1. if (i+1 < test_size) { ASSERT_EQ(i+1, mmbit_iterate_bounded(ba, test_size, i+1, i+2)); } // Scanning from i+1 to the end should find i+1. if (i+1 < test_size) { ASSERT_EQ(i+1, mmbit_iterate_bounded(ba, test_size, i+1, test_size)); } } } TEST_P(MultiBitTest, Set) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); mmbit_clear(ba, test_size); ASSERT_FALSE(mmbit_any(ba, test_size)); for (u64a i = 0; i < test_size; i += stride) { SCOPED_TRACE(i); // set a bit that wasn't set before ASSERT_FALSE(mmbit_isset(ba, test_size, i)); ASSERT_FALSE(mmbit_set(ba, test_size, i)); ASSERT_TRUE(mmbit_isset(ba, test_size, i)); // set it again, should return true ASSERT_TRUE(mmbit_set(ba, test_size, i)); // clear it and make sure it's gone mmbit_unset(ba, test_size, i); ASSERT_FALSE(mmbit_isset(ba, test_size, i)); // set it again, should return false ASSERT_FALSE(mmbit_set(ba, test_size, i)); } } TEST_P(MultiBitTest, Iter) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); mmbit_clear(ba, test_size); ASSERT_EQ(MMB_INVALID, mmbit_iterate(ba, test_size, MMB_INVALID)); for (u64a i = 0; i < test_size; i += stride) { SCOPED_TRACE(i); mmbit_clear(ba, test_size); mmbit_set(ba, test_size, i); ASSERT_EQ(i, mmbit_iterate(ba, test_size, MMB_INVALID)); ASSERT_EQ(MMB_INVALID, mmbit_iterate(ba, test_size, i)); } } TEST_P(MultiBitTest, IterAll) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); mmbit_clear(ba, test_size); ASSERT_EQ(MMB_INVALID, mmbit_iterate(ba, test_size, MMB_INVALID)); // Set all bits. for (u64a i = 0; i < test_size; i += stride) { mmbit_set(ba, test_size, i); } // Find all bits. u32 it = MMB_INVALID; for (u64a i = 0; i < test_size; i += stride) { ASSERT_EQ(i, mmbit_iterate(ba, test_size, it)); it = i; } } TEST_P(MultiBitTest, AnyPrecise) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); mmbit_clear(ba, test_size); ASSERT_FALSE(mmbit_any_precise(ba, test_size)); for (u64a i = 0; i < test_size; i += stride) { SCOPED_TRACE(i); mmbit_clear(ba, test_size); mmbit_set(ba, test_size, i); ASSERT_TRUE(mmbit_any_precise(ba, test_size)); } } TEST_P(MultiBitTest, Any) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); mmbit_clear(ba, test_size); ASSERT_FALSE(mmbit_any(ba, test_size)); for (u64a i = 0; i < test_size; i += stride) { SCOPED_TRACE(i); mmbit_clear(ba, test_size); mmbit_set(ba, test_size, i); ASSERT_TRUE(mmbit_any(ba, test_size)); } } TEST_P(MultiBitTest, All) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); mmbit_clear(ba, test_size); ASSERT_FALSE(mmbit_all(ba, test_size)); for (u64a i = 0; i < test_size - 1; i += stride) { SCOPED_TRACE(i); mmbit_set(ba, test_size, i); ASSERT_FALSE(mmbit_all(ba, test_size)); } // Set all bits. fill_mmbit(ba, test_size); ASSERT_TRUE(mmbit_all(ba, test_size)); } TEST_P(MultiBitTest, UnsetRange1) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); // Set all bits. fill_mmbit(ba, test_size); // Use mmbit_unset_range to switch off any single bit. for (u64a i = 0; i < test_size; i += stride) { SCOPED_TRACE(i); ASSERT_TRUE(mmbit_isset(ba, test_size, i)); mmbit_unset_range(ba, test_size, i, i + 1); ASSERT_FALSE(mmbit_isset(ba, test_size, i)); } } TEST_P(MultiBitTest, UnsetRange2) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); // XXX: slow on large cases, so we skip those. if (stride > 1) { return; } // Set all bits. fill_mmbit(ba, test_size); // Use mmbit_unset_range to switch off all bits. mmbit_unset_range(ba, test_size, 0, test_size); for (u64a i = 0; i < test_size; i += stride) { SCOPED_TRACE(i); ASSERT_FALSE(mmbit_isset(ba, test_size, i)); } } TEST_P(MultiBitTest, UnsetRange3) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); // Use mmbit_unset_range to switch off bits in chunks of 3. for (u64a i = 0; i < test_size - 3; i += stride) { // Switch on the bit before, the bits in question, and the bit after. if (i > 0) { mmbit_set(ba, test_size, i - 1); } for (u64a j = i; j < min(i + 4, (u64a)test_size); j++) { mmbit_set(ba, test_size, j); } // Switch off the chunk. mmbit_unset_range(ba, test_size, i, i + 3); ASSERT_FALSE(mmbit_isset(ba, test_size, i)); ASSERT_FALSE(mmbit_isset(ba, test_size, i + 1)); ASSERT_FALSE(mmbit_isset(ba, test_size, i + 2)); // Bits on either side should be intact. if (i > 0) { ASSERT_TRUE(mmbit_isset(ba, test_size, i - 1)); } if (i + 3 < test_size) { ASSERT_TRUE(mmbit_isset(ba, test_size, i + 3)); } } } TEST_P(MultiBitTest, InitRangeAll) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); // Use mmbit_init_range to set all the bits. mmbit_init_range(ba, test_size, 0, test_size); // Make sure they're all set. for (u64a i = 0; i < test_size; i += stride) { SCOPED_TRACE(i); ASSERT_TRUE(mmbit_isset(ba, test_size, i)); } } TEST_P(MultiBitTest, InitRangeNone) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); // Use mmbit_set_range to set all the bits. mmbit_init_range(ba, test_size, 0, 0); // Make sure nothing's set. ASSERT_EQ(MMB_INVALID, mmbit_iterate(ba, test_size, MMB_INVALID)); } TEST_P(MultiBitTest, InitRangeOne) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); for (u64a i = 0; i < test_size; i += stride) { mmbit_init_range(ba, test_size, i, i + 1); // Only bit 'i' should be on. ASSERT_EQ(i, mmbit_iterate(ba, test_size, MMB_INVALID)); ASSERT_EQ(MMB_INVALID, mmbit_iterate(ba, test_size, i)); } } TEST_P(MultiBitTest, InitRangeChunked) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); // Init ranges chunk by chunk. for (u32 n = 2; n <= 10; n++) { u32 chunk_size = test_size / n; if (chunk_size == 0) { break; } for (u32 k = 0; k < n; k++) { u32 chunk_begin = k * chunk_size; u32 chunk_end = min(test_size, (k + 1) * chunk_size); mmbit_init_range(ba, test_size, chunk_begin, chunk_end); // First bit set should be chunk_begin. ASSERT_EQ(chunk_begin, mmbit_iterate(ba, test_size, MMB_INVALID)); // All bits in the chunk should be on. for (u64a i = chunk_begin; i < chunk_end; i += stride) { SCOPED_TRACE(i); ASSERT_TRUE(mmbit_isset(ba, test_size, i)); } // Last bit on is chunk_end - 1. if (chunk_end) { ASSERT_EQ(MMB_INVALID, mmbit_iterate(ba, test_size, chunk_end - 1)); } } } } static void applyMB(const scatter_plan_raw &sp, u8 *out) { for (const auto &e : sp.p_u64a) { memcpy(out + e.offset, &e.val, sizeof(e.val)); } for (const auto &e : sp.p_u32) { memcpy(out + e.offset, &e.val, sizeof(e.val)); } for (const auto &e : sp.p_u16) { memcpy(out + e.offset, &e.val, sizeof(e.val)); } for (const auto &e : sp.p_u8) { memcpy(out + e.offset, &e.val, sizeof(e.val)); } } TEST_P(MultiBitTest, InitRangePlanChunked) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); // Init ranges chunk by chunk. for (u32 n = 2; n <= 10; n++) { u32 chunk_size = test_size / n; if (chunk_size == 0) { break; } for (u32 k = 0; k < n; k++) { u32 chunk_begin = k * chunk_size; u32 chunk_end = min(test_size, (k + 1) * chunk_size); scatter_plan_raw sp; mmbBuildInitRangePlan(test_size, chunk_begin, chunk_end, &sp); memset(ba, 0xaa, mmbit_size(test_size)); applyMB(sp, ba); // First bit set should be chunk_begin. ASSERT_EQ(chunk_begin, mmbit_iterate(ba, test_size, MMB_INVALID)); // All bits in the chunk should be on. for (u64a i = chunk_begin; i < chunk_end; i += stride) { SCOPED_TRACE(i); ASSERT_TRUE(mmbit_isset(ba, test_size, i)); } // Last bit on is chunk_end - 1. if (chunk_end) { ASSERT_EQ(MMB_INVALID, mmbit_iterate(ba, test_size, chunk_end - 1)); } } } } TEST(MultiBit, SparseIteratorBegin1) { const u32 test_size = 100; vector bits; bits.push_back(1); bits.push_back(5); bits.push_back(27); bits.push_back(35); bits.push_back(68); auto it = mmbBuildSparseIterator(bits, test_size); //ASSERT_EQ(4U, it.size()); // Trivial initial test: all bits in 'bits' are on, all others are off mmbit_holder ba(test_size); mmbit_clear(ba, test_size); for (size_t i = 0; i < bits.size(); i++) { mmbit_set(ba, test_size, bits[i]); } vector state(mmbit_sparse_iter_state_size(test_size)); for (u32 i = 0; i < bits.size(); i++) { u32 val, idx = 0xffffffff; val = mmbit_sparse_iter_begin(ba, test_size, &idx, &it[0], &state[0]); ASSERT_EQ(bits[i], val); ASSERT_EQ(i, idx); mmbit_unset(ba, test_size, bits[i]); ASSERT_FALSE(mmbit_isset(ba, test_size, bits[i])); } // All clear, begin should be invalid u32 val, idx = 0xffffffff; val = mmbit_sparse_iter_begin(ba, test_size, &idx, &it[0], &state[0]); ASSERT_EQ(MMB_INVALID, val); } TEST(MultiBit, SparseIteratorBegin2) { const u32 test_size = 40000; vector bits; bits.push_back(1); bits.push_back(256); bits.push_back(257); bits.push_back(1024); bits.push_back(8920); bits.push_back(37000); auto it = mmbBuildSparseIterator(bits, test_size); //ASSERT_EQ(12U, it.size()); // Trivial initial test: all bits in 'bits' are on, all others are off mmbit_holder ba(test_size); mmbit_clear(ba, test_size); for (size_t i = 0; i < bits.size(); i++) { mmbit_set(ba, test_size, bits[i]); } vector state(mmbit_sparse_iter_state_size(test_size)); for (u32 i = 0; i < bits.size(); i++) { u32 val, idx = 0xffffffff; val = mmbit_sparse_iter_begin(ba, test_size, &idx, &it[0], &state[0]); ASSERT_EQ(bits[i], val); ASSERT_EQ(i, idx); mmbit_unset(ba, test_size, bits[i]); ASSERT_FALSE(mmbit_isset(ba, test_size, bits[i])); } // All clear, begin should be invalid u32 val, idx = 0xffffffff; val = mmbit_sparse_iter_begin(ba, test_size, &idx, &it[0], &state[0]); ASSERT_EQ(MMB_INVALID, val); } TEST(MultiBit, SparseIteratorNext1) { const u32 test_size = 100; vector bits; bits.push_back(1); bits.push_back(5); bits.push_back(27); bits.push_back(35); bits.push_back(68); auto it = mmbBuildSparseIterator(bits, test_size); // Trivial initial test: all bits in 'bits' are on, all others are off mmbit_holder ba(test_size); mmbit_clear(ba, test_size); for (size_t i = 0; i < bits.size(); i++) { mmbit_set(ba, test_size, bits[i]); } vector state(mmbit_sparse_iter_state_size(test_size)); u32 val, idx = 0xffffffff; val = mmbit_sparse_iter_begin(ba, test_size, &idx, &it[0], &state[0]); ASSERT_EQ(bits[0], val); ASSERT_EQ(0U, idx); for (u32 i = 1; i < bits.size(); i++) { val = mmbit_sparse_iter_next(ba, test_size, val, &idx, &it[0], &state[0]); ASSERT_EQ(bits[i], val); ASSERT_EQ(i, idx); } // Same test with all bits on for (size_t i = 0; i < test_size; i++) { mmbit_set(ba, test_size, i); } val = mmbit_sparse_iter_begin(ba, test_size, &idx, &it[0], &state[0]); ASSERT_EQ(bits[0], val); ASSERT_EQ(0U, idx); for (u32 i = 1; i < bits.size(); i++) { val = mmbit_sparse_iter_next(ba, test_size, val, &idx, &it[0], &state[0]); ASSERT_EQ(bits[i], val); ASSERT_EQ(i, idx); } // Test with only the last half on mmbit_clear(ba, test_size); size_t last_half = bits.size()/2; for (size_t i = last_half; i < bits.size(); i++) { mmbit_set(ba, test_size, bits[i]); } val = mmbit_sparse_iter_begin(ba, test_size, &idx, &it[0], &state[0]); ASSERT_EQ(bits[last_half], val); ASSERT_EQ(last_half, idx); for (u32 i = last_half + 1; i < bits.size(); i++) { val = mmbit_sparse_iter_next(ba, test_size, val, &idx, &it[0], &state[0]); ASSERT_EQ(bits[i], val); ASSERT_EQ(i, idx); } } TEST(MultiBit, SparseIteratorNext2) { const u32 test_size = 40000; vector bits; bits.push_back(1); bits.push_back(256); bits.push_back(257); bits.push_back(1024); bits.push_back(1025); bits.push_back(4095); bits.push_back(4096); bits.push_back(4097); bits.push_back(8920); bits.push_back(37000); bits.push_back(39999); auto it = mmbBuildSparseIterator(bits, test_size); // Trivial initial test: all bits in 'bits' are on, all others are off mmbit_holder ba(test_size); mmbit_clear(ba, test_size); for (size_t i = 0; i < bits.size(); i++) { mmbit_set(ba, test_size, bits[i]); } vector state(mmbit_sparse_iter_state_size(test_size)); u32 val, idx = 0xffffffff; val = mmbit_sparse_iter_begin(ba, test_size, &idx, &it[0], &state[0]); ASSERT_EQ(bits[0], val); ASSERT_EQ(0U, idx); for (u32 i = 1; i < bits.size(); i++) { val = mmbit_sparse_iter_next(ba, test_size, val, &idx, &it[0], &state[0]); ASSERT_EQ(bits[i], val); ASSERT_EQ(i, idx); } // Same test with all bits on for (size_t i = 0; i < test_size; i++) { mmbit_set(ba, test_size, i); } val = mmbit_sparse_iter_begin(ba, test_size, &idx, &it[0], &state[0]); ASSERT_EQ(bits[0], val); ASSERT_EQ(0U, idx); for (u32 i = 1; i < bits.size(); i++) { val = mmbit_sparse_iter_next(ba, test_size, val, &idx, &it[0], &state[0]); ASSERT_EQ(bits[i], val); ASSERT_EQ(i, idx); } // Test with only the last half on mmbit_clear(ba, test_size); size_t last_half = bits.size()/2; for (size_t i = last_half; i < bits.size(); i++) { mmbit_set(ba, test_size, bits[i]); } val = mmbit_sparse_iter_begin(ba, test_size, &idx, &it[0], &state[0]); ASSERT_EQ(bits[last_half], val); ASSERT_EQ(last_half, idx); for (u32 i = last_half + 1; i < bits.size(); i++) { val = mmbit_sparse_iter_next(ba, test_size, val, &idx, &it[0], &state[0]); ASSERT_EQ(bits[i], val); ASSERT_EQ(i, idx); } } TEST(MultiBit, SparseIteratorNextSmall) { const u32 test_size = 15; vector bits; bits.push_back(1); bits.push_back(3); bits.push_back(6); bits.push_back(7); bits.push_back(12); bits.push_back(14); auto it = mmbBuildSparseIterator(bits, test_size); // Trivial initial test: all bits in 'bits' are on, all others are off mmbit_holder ba(test_size); mmbit_clear(ba, test_size); for (size_t i = 0; i < bits.size(); i++) { mmbit_set(ba, test_size, bits[i]); } vector state(mmbit_sparse_iter_state_size(test_size)); u32 val, idx = 0xffffffff; val = mmbit_sparse_iter_begin(ba, test_size, &idx, &it[0], &state[0]); ASSERT_EQ(bits[0], val); ASSERT_EQ(0U, idx); for (u32 i = 1; i < bits.size(); i++) { val = mmbit_sparse_iter_next(ba, test_size, val, &idx, &it[0], &state[0]); ASSERT_EQ(bits[i], val); ASSERT_EQ(i, idx); } // Same test with all bits on for (size_t i = 0; i < test_size; i++) { mmbit_set(ba, test_size, i); } val = mmbit_sparse_iter_begin(ba, test_size, &idx, &it[0], &state[0]); ASSERT_EQ(bits[0], val); ASSERT_EQ(0U, idx); for (u32 i = 1; i < bits.size(); i++) { val = mmbit_sparse_iter_next(ba, test_size, val, &idx, &it[0], &state[0]); ASSERT_EQ(bits[i], val); ASSERT_EQ(i, idx); } // Test with only the last half on mmbit_clear(ba, test_size); size_t last_half = bits.size()/2; for (size_t i = last_half; i < bits.size(); i++) { mmbit_set(ba, test_size, bits[i]); } val = mmbit_sparse_iter_begin(ba, test_size, &idx, &it[0], &state[0]); ASSERT_EQ(bits[last_half], val); ASSERT_EQ(last_half, idx); for (u32 i = last_half + 1; i < bits.size(); i++) { val = mmbit_sparse_iter_next(ba, test_size, val, &idx, &it[0], &state[0]); ASSERT_EQ(bits[i], val); ASSERT_EQ(i, idx); } } TEST_P(MultiBitTest, SparseIteratorBeginAll) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); // Put all our bits into the sparse iterator. vector bits; bits.reserve(test_size / stride); for (u64a i = 0; i < test_size; i += stride) { bits.push_back(i); } auto it = mmbBuildSparseIterator(bits, test_size); // Switch all bits on in state. mmbit_clear(ba, test_size); ASSERT_FALSE(mmbit_any(ba, test_size)); fill_mmbit(ba, test_size); // Iterate over all bits; clear the first one and ensure that the iterator // reports the next bit as the start bit. vector state(mmbit_sparse_iter_state_size(test_size)); u32 val, idx = 0xffffffff; for (size_t i = 0, num_keys = bits.size(); i < num_keys; i++) { val = mmbit_sparse_iter_begin(ba, test_size, &idx, &it[0], &state[0]); ASSERT_EQ(bits[i], val); ASSERT_EQ(i, idx); mmbit_unset(ba, test_size, bits[i]); } // Cleared all bits, iterator should return invalid. val = mmbit_sparse_iter_begin(ba, test_size, &idx, &it[0], &state[0]); ASSERT_EQ(MMB_INVALID, val); } TEST_P(MultiBitTest, SparseIteratorBeginThirds) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); // XXX: only run on stride=1 cases for speed. if (stride > 1) { return; } // Put all our bits into the sparse iterator vector bits(test_size); for (u32 i = 0; i != test_size; i++) { bits[i] = i; } auto it = mmbBuildSparseIterator(bits, test_size); // Switch every third bits on in state mmbit_clear(ba, test_size); ASSERT_FALSE(mmbit_any(ba, test_size)); for (u64a i = 0; i < test_size; i += 3) { mmbit_set(ba, test_size, i); } // Iterate over all bits; clear the first on bit and ensure that the // iterator reports the next on bit as the start bit. vector state(mmbit_sparse_iter_state_size(test_size)); u32 val, idx = 0xffffffff; val = mmbit_sparse_iter_begin(ba, test_size, &idx, &it[0], &state[0]); ASSERT_EQ(0U, val); ASSERT_EQ(0U, idx); for (u64a i = 0; i < test_size - 3; i += 3) { mmbit_unset(ba, test_size, i); val = mmbit_sparse_iter_begin(ba, test_size, &idx, &it[0], &state[0]); ASSERT_EQ(i+3, val); ASSERT_EQ(i+3, idx); } } TEST_P(MultiBitTest, SparseIteratorNextAll) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); // Put all our bits into the sparse iterator. vector bits; bits.reserve(test_size / stride); for (u64a i = 0; i < test_size; i += stride) { bits.push_back(i); } auto it = mmbBuildSparseIterator(bits, test_size); // Switch all bits on in state mmbit_clear(ba, test_size); ASSERT_FALSE(mmbit_any(ba, test_size)); fill_mmbit(ba, test_size); // Iterate over all bits. vector state(mmbit_sparse_iter_state_size(test_size)); u32 val, idx = 0xffffffff; val = mmbit_sparse_iter_begin(ba, test_size, &idx, &it[0], &state[0]); ASSERT_EQ(0U, val); ASSERT_EQ(0U, idx); for (u32 i = 1, num_keys = bits.size(); i < num_keys; i++) { val = mmbit_sparse_iter_next(ba, test_size, val, &idx, &it[0], &state[0]); ASSERT_EQ(bits[i], val); ASSERT_EQ(i, idx); } // All bits tested, we should now return invalid val = mmbit_sparse_iter_next(ba, test_size, bits.back(), &idx, &it[0], &state[0]); ASSERT_EQ(MMB_INVALID, val); } TEST_P(MultiBitTest, SparseIteratorNextExactStrided) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); if (stride == 1) { // This test is the same as SparseIteratorNextAll for stride 1 return; } // Put all our bits into the sparse iterator and switch them on in the // state. mmbit_clear(ba, test_size); vector bits; bits.reserve(test_size / stride); for (u64a i = 0; i < test_size; i += stride) { bits.push_back(i); mmbit_set(ba, test_size, i); } auto it = mmbBuildSparseIterator(bits, test_size); // Iterate over all bits. vector state(mmbit_sparse_iter_state_size(test_size)); u32 val, idx = 0xffffffff; val = mmbit_sparse_iter_begin(ba, test_size, &idx, &it[0], &state[0]); ASSERT_EQ(0U, val); ASSERT_EQ(0U, idx); for (u32 i = 1, num_keys = bits.size(); i < num_keys; i++) { val = mmbit_sparse_iter_next(ba, test_size, val, &idx, &it[0], &state[0]); ASSERT_EQ(bits[i], val); ASSERT_EQ(i, idx); } // All bits tested, we should now return invalid val = mmbit_sparse_iter_next(ba, test_size, bits.back(), &idx, &it[0], &state[0]); ASSERT_EQ(MMB_INVALID, val); } TEST_P(MultiBitTest, SparseIteratorNextNone) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); // Put all our bits into the sparse iterator. vector bits; bits.reserve(test_size / stride); for (u64a i = 0; i < test_size; i += stride) { bits.push_back(i); } auto it = mmbBuildSparseIterator(bits, test_size); // Switch only the first bit on mmbit_clear(ba, test_size); mmbit_set(ba, test_size, 0); // Begin should find first bit vector state(mmbit_sparse_iter_state_size(test_size)); u32 val, idx = 0xffffffff; val = mmbit_sparse_iter_begin(ba, test_size, &idx, &it[0], &state[0]); ASSERT_EQ(0U, val); ASSERT_EQ(0U, idx); // Next should return invalid val = mmbit_sparse_iter_next(ba, test_size, val, &idx, &it[0], &state[0]); ASSERT_EQ(MMB_INVALID, val); } TEST_P(MultiBitTest, SparseIteratorUnsetAll) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); // Put all our bits into the sparse iterator vector bits; bits.reserve(test_size / stride); for (u64a i = 0; i < test_size; i += stride) { bits.push_back(i); } auto it = mmbBuildSparseIterator(bits, test_size); // Switch all bits on mmbit_clear(ba, test_size); fill_mmbit(ba, test_size); // Run iterator unset, which should clear all bits struct mmbit_sparse_state sparse_iter_state[MAX_SPARSE_ITER_STATES]; mmbit_sparse_iter_unset(ba, test_size, &it[0], sparse_iter_state); // All bits should now be off for (u32 i = 0, num_keys = bits.size(); i < num_keys; i++) { ASSERT_FALSE(mmbit_isset(ba, test_size, bits[i])); } } TEST_P(MultiBitTest, SparseIteratorUnsetHalves) { SCOPED_TRACE(test_size); ASSERT_TRUE(ba != nullptr); // TODO: write useful striding version of this test. if (stride > 1) { return; } // Two sparse iterators: one for even bits, one for odd ones vector even, odd; for (u64a i = 0; i < test_size; i += 2) { even.push_back(i); } for (u64a i = 1; i < test_size; i += 2) { odd.push_back(i); } auto it_even = mmbBuildSparseIterator(even, test_size); auto it_odd = mmbBuildSparseIterator(odd, test_size); // Switch all bits on mmbit_clear(ba, test_size); for (u32 i = 0; i != test_size; i++) { mmbit_set(ba, test_size, i); ASSERT_TRUE(mmbit_isset(ba, test_size, i)); } // Run even iterator unset, which should clear all even bits struct mmbit_sparse_state sparse_iter_state[MAX_SPARSE_ITER_STATES]; mmbit_sparse_iter_unset(ba, test_size, &it_even[0], sparse_iter_state); // All even bits are off, all odd bits are on for (u32 i = 0; i != test_size; i++) { if ((i % 2) == 0) { ASSERT_FALSE(mmbit_isset(ba, test_size, i)); } else { ASSERT_TRUE(mmbit_isset(ba, test_size, i)); } } // Run odd iterator unset, clearing the odd bits mmbit_sparse_iter_unset(ba, test_size, &it_odd[0], sparse_iter_state); // All bits should now be off for (u32 i = 0; i != test_size; i++) { ASSERT_FALSE(mmbit_isset(ba, test_size, i)); } } static const MultiBitTestParam multibitTests[] = { // We provide both test size and stride so that larger tests don't take // forever checking every single key. // Small cases, stride 1. { 4, 1 }, { 7, 1 }, { 8, 1 }, { 13, 1 }, { 16, 1 }, { 17, 1 }, { 32, 1 }, { 33, 1 }, { 57, 1 }, { 64, 1 }, { 65, 1 }, { 100, 1 }, { 128, 1 }, { 200, 1 }, { 256, 1 }, { 302, 1 }, { 1024, 1 }, { 1025, 1 }, { 2099, 1 }, #ifdef NDEBUG { 10000, 1 }, { 32768, 1 }, { 32769, 1 }, { 200000, 1 }, #endif // Larger cases, bigger strides. { 1U << 18, 3701 }, { 1U << 19, 3701 }, { 1U << 20, 3701 }, { 1U << 21, 3701 }, #ifdef NDEBUG { 1U << 22, 3701 }, { 1U << 23, 3701 }, { 1U << 24, 3701 }, { 1U << 25, 3701 }, { 1U << 26, 3701 }, { 1U << 27, 7919 }, { 1U << 28, 15073 }, { 1U << 29, 24413 }, { 1U << 30, 50377 }, { 1U << 31, 104729 }, #endif }; INSTANTIATE_TEST_CASE_P(MultiBit, MultiBitTest, ValuesIn(multibitTests)); TEST(MultiBit, SizeTooBig) { ASSERT_NO_THROW(mmbit_size(MMB_MAX_BITS)); ASSERT_THROW(mmbit_size(MMB_MAX_BITS + 1), ResourceLimitError); }