diff --git a/.clang-format b/.clang-format index 53aa216..17288a3 100644 --- a/.clang-format +++ b/.clang-format @@ -3,10 +3,9 @@ BasedOnStyle: Google IndentWidth: 4 TabWidth: 4 UseTab: Never -AllowShortIfStatementsOnASingleLine: false BreakBeforeBraces: Attach ColumnLimit: 80 ContinuationIndentWidth: 4 IndentCaseLabels: true AccessModifierOffset: -4 -AllowShortIfStatementsOnASingleLine: true \ No newline at end of file +AllowShortIfStatementsOnASingleLine: Always \ No newline at end of file diff --git a/lib/array_container.h b/lib/array_container.h index 2490194..6504c1b 100644 --- a/lib/array_container.h +++ b/lib/array_container.h @@ -11,89 +11,112 @@ namespace froaring { template class ArrayContainer { - using DataType = froaring::can_fit_t; + using IndexOrNumType = froaring::can_fit_t; + using SizeType = froaring::can_fit_t; public: - ArrayContainer() : capacity(ARRAY_CONTAINER_INIT_SIZE), size(0) { - vals = new DataType[capacity]; + void debug_print() { + for (SizeType i = 0; i < size; ++i) { + std::cout << int(vals[i]) << " "; + } + std::cout << std::endl; + } + static ArrayContainer* create( + SizeType capacity = ARRAY_CONTAINER_INIT_SIZE) { + size_t totalSize = + sizeof(ArrayContainer) + capacity * sizeof(IndexOrNumType); + void* memory = operator new(totalSize); + ArrayContainer* container = new (memory) ArrayContainer(); + return container; } - - ~ArrayContainer() { delete[] vals; } ArrayContainer(const ArrayContainer&) = delete; ArrayContainer& operator=(const ArrayContainer&) = delete; void clear() { size = 0; } - void set(DataType num) { - auto pos = (size ? lower_bound(num) : 0); - if (pos < size && vals[pos] == num) return; + static void set(ArrayContainer*& c, IndexOrNumType num) { + auto pos = (c->size ? c->lower_bound(num) : 0); + if (pos < c->size && c->vals[pos] == num) return; - if (size == capacity) expand(); + if (c->size == c->capacity) expand(c); - std::memmove(&vals[pos + 1], &vals[pos], - (size - pos) * sizeof(DataType)); + std::memmove(&c->vals[pos + 1], &c->vals[pos], + (c->size - pos) * + sizeof(IndexOrNumType)); // TODO: Boost by combining + // memmove with expand() - vals[pos] = num; - ++size; + c->vals[pos] = num; + ++c->size; } - void reset(DataType num) { + void reset(IndexOrNumType num) { if (!size) return; auto pos = lower_bound(num); if (pos == size || vals[pos] != num) return; std::memmove(&vals[pos], &vals[pos + 1], - (size - pos - 1) * sizeof(DataType)); + (size - pos - 1) * sizeof(IndexOrNumType)); --size; } - bool test(DataType num) const { + bool test(IndexOrNumType num) const { if (!size) return false; auto pos = lower_bound(num); + std::cout << "pos=" << int(pos) << " v[p]=" << int(vals[pos]) + << std::endl; return pos < size && vals[pos] == num; } - bool test_and_set(DataType num) { + static bool test_and_set(ArrayContainer*& c, IndexOrNumType num) { bool was_set; - DataType pos; - if (!size) { + IndexOrNumType pos; + if (!c->size) { was_set = false; } else { - pos = lower_bound(num); - was_set = (pos < size && vals[pos] == num); + pos = c->lower_bound(num); + was_set = (pos < c->size && c->vals[pos] == num); } if (was_set) return false; - if (size == capacity) expand(); + if (c->size == c->capacity) expand(c); - std::memmove(&vals[pos + 1], &vals[pos], - (size - pos) * sizeof(DataType)); + std::memmove(&c->vals[pos + 1], &c->vals[pos], + (c->size - pos) * sizeof(IndexOrNumType)); - vals[pos] = num; - ++size; + c->vals[pos] = num; + ++c->size; return true; } - DataType cardinality() const { return size; } + SizeType cardinality() const { return size; } private: - void expand() { - capacity *= 2; - auto new_vals = new DataType[capacity]; - std::memmove(new_vals, vals, size * sizeof(DataType)); - delete[] vals; - vals = new_vals; + static void expand(ArrayContainer*& c) { + auto new_cap = c->capacity * 2; + + size_t totalSize = + sizeof(ArrayContainer) + new_cap * sizeof(IndexOrNumType); + void* new_memory = operator new(totalSize); + ArrayContainer* new_container = + new (new_memory) ArrayContainer(new_cap, c->size); + + std::memmove(&new_container->vals, &c->vals, + c->size * sizeof(IndexOrNumType)); + c->~ArrayContainer(); + operator delete(c); + + c = new_container; } - DataType lower_bound(DataType num) const { + IndexOrNumType lower_bound(IndexOrNumType num) const { assert(size && "Cannot find lower bound in an empty container"); - DataType left = 0; - DataType right = size; + SizeType left = 0; + SizeType right = size; while (left < right) { - DataType mid = left + (right - left) / 2; + SizeType mid = left + (right - left) / 2; if (vals[mid] < num) { left = mid + 1; } else { @@ -103,8 +126,12 @@ class ArrayContainer { return left; } - DataType* vals; - size_t capacity; // TODO: use less memory by using a smaller type - size_t size; + ArrayContainer(SizeType capacity = ARRAY_CONTAINER_INIT_SIZE, + SizeType size = 0) + : capacity(capacity), size(size) {} + + SizeType capacity; + SizeType size; + IndexOrNumType vals[]; }; } // namespace froaring \ No newline at end of file diff --git a/lib/bitmap_container.h b/lib/bitmap_container.h index 0c6bf12..cda06d8 100644 --- a/lib/bitmap_container.h +++ b/lib/bitmap_container.h @@ -15,41 +15,48 @@ class BitmapContainer { static constexpr size_t TotalBits = (1 << DataBits); static constexpr size_t WordCount = (TotalBits + BitsPerWord - 1) / BitsPerWord; // ceiling - std::array bits; - using DataType = froaring::can_fit_t; - - BitmapContainer() { bits.fill(0); } + using IndexOrNumType = froaring::can_fit_t; + using SizeType = froaring::can_fit_t; BitmapContainer(const BitmapContainer&) = delete; BitmapContainer& operator=(const BitmapContainer&) = delete; void clear() { std::memset(bits.data(), 0, WordCount * sizeof(WordType)); } - void set(DataType index) { + static BitmapContainer* create() { + BitmapContainer* container = new BitmapContainer(); + return container; + } + + void set(IndexOrNumType index) { bits[index / BitsPerWord] |= (1 << (index % BitsPerWord)); } - bool test(DataType index) const { + bool test(IndexOrNumType index) const { return bits[index / BitsPerWord] & (1 << (index % BitsPerWord)); } - bool test_and_set(DataType index) { + bool test_and_set(IndexOrNumType index) { bool was_set = test(index); if (was_set) return false; set(index); return true; } - void reset(DataType index) { + void reset(IndexOrNumType index) { bits[index / BitsPerWord] &= ~(1 << (index % BitsPerWord)); } - size_t cardinality() const { - size_t count = 0; + SizeType cardinality() const { + SizeType count = 0; for (const auto& word : bits) { count += __builtin_popcount(word); } return count; } + +private: + BitmapContainer() { memset(bits, 0, sizeof(bits)); } + WordType bits[WordCount]; }; } // namespace froaring \ No newline at end of file diff --git a/lib/containers.h b/lib/containers.h index d765be4..f1d0fd7 100644 --- a/lib/containers.h +++ b/lib/containers.h @@ -132,7 +132,8 @@ class Containers { ContainerEntry newEntry; newEntry.containerIndex = index; newEntry.containerType = CTy::Array; - newEntry.containerPtr = new ArrayContainer(); + newEntry.containerPtr = + ArrayContainer::create(); containers.insert(it, newEntry); it = getContainerPosByIndex(index); @@ -145,21 +146,26 @@ class Containers { // Now we found the corresponding container switch (entry.containerType) { - case CTy::RLE: - static_cast*>( - entry.containerPtr) - ->set(data); + case CTy::RLE: { + auto rleptr = static_cast*>( + entry.containerPtr); + RLEContainer::set(rleptr, data); + entry.containerPtr = (void*)rleptr; break; - case CTy::Array: - static_cast*>( - entry.containerPtr) - ->set(data); + } + case CTy::Array: { + auto aptr = static_cast*>( + entry.containerPtr); + ArrayContainer::set(aptr, data); + entry.containerPtr = (void*)aptr; break; - case CTy::Bitmap: + } + case CTy::Bitmap: { static_cast*>( entry.containerPtr) ->set(data); break; + } default: FROARING_UNREACHABLE } diff --git a/lib/froaring.h b/lib/froaring.h index 1e1401b..a027959 100644 --- a/lib/froaring.h +++ b/lib/froaring.h @@ -20,15 +20,17 @@ template class FlexibleRoaringBitmap { using IndexType = froaring::can_fit_t; - + using RLESized = RLEContainer; + using BitmapSized = BitmapContainer; + using ArraySized = ArrayContainer; IndexType start_index = 0; // The index of the container. This is only used // if the bitmap is storing a single container. ContainerType type; union { // If only a single container is needed, we can store it directly. - RLEContainer rleContainer; - ArrayContainer arrayContainer; - BitmapContainer bitmapContainer; + RLESized* rleContainer; + ArraySized* arrayContainer; + BitmapSized* bitmapContainer; // Otherwise, the bit map is of containers. Containers containers; }; @@ -37,7 +39,7 @@ class FlexibleRoaringBitmap { public: /// We start from an array container. FlexibleRoaringBitmap() : type(CTy::Array) { - new (&arrayContainer) ArrayContainer(); + arrayContainer = ArraySized::create(); } ~FlexibleRoaringBitmap() { destroyCurrentContainer(); } @@ -45,20 +47,20 @@ class FlexibleRoaringBitmap { void set(WordType index) { switch (type) { case CTy::RLE: - rleContainer.set(index); - if (arrayContainer.cardinality() > DataBits) { + RLESized::set(rleContainer, index); + if (rleContainer->cardinality() > DataBits) { switchToContainers(); } break; case CTy::Array: - arrayContainer.set(index); - if (arrayContainer.cardinality() > DataBits) { + ArraySized::set(arrayContainer, index); + if (arrayContainer->cardinality() > DataBits) { switchToContainers(); } break; case CTy::Bitmap: - bitmapContainer.set(index); - if (arrayContainer.cardinality() > DataBits) { + bitmapContainer->set(index); + if (bitmapContainer->cardinality() > DataBits) { switchToContainers(); } break; @@ -73,11 +75,11 @@ class FlexibleRoaringBitmap { bool test(WordType index) const { switch (type) { case CTy::RLE: - return rleContainer.test(index); + return rleContainer->test(index); case CTy::Array: - return arrayContainer.test(index); + return arrayContainer->test(index); case CTy::Bitmap: - return bitmapContainer.test(index); + return bitmapContainer->test(index); case CTy::Containers: return containers.test(index); default: @@ -90,11 +92,11 @@ class FlexibleRoaringBitmap { size_t cardinality() const { switch (type) { case CTy::RLE: - return rleContainer.cardinality(); + return rleContainer->cardinality(); case CTy::Array: - return arrayContainer.cardinality(); + return arrayContainer->cardinality(); case CTy::Bitmap: - return bitmapContainer.cardinality(); + return bitmapContainer->cardinality(); case CTy::Containers: return containers.cardinality(); default: @@ -107,13 +109,13 @@ class FlexibleRoaringBitmap { void destroyCurrentContainer() { switch (type) { case CTy::RLE: - rleContainer.~RLEContainer(); + rleContainer->~RLEContainer(); break; case CTy::Array: - arrayContainer.~ArrayContainer(); + arrayContainer->~ArrayContainer(); break; case CTy::Bitmap: - bitmapContainer.~BitmapContainer(); + bitmapContainer->~BitmapContainer(); break; case CTy::Containers: containers.~Containers(); @@ -131,11 +133,7 @@ class FlexibleRoaringBitmap { type == CTy::Array && "Only array can switch to containers (otherwise not implemented)"); Containers newContainers; - // TODO: newContainers.add(arrayContainer); - destroyCurrentContainer(); - new (&containers) - Containers(std::move(newContainers)); - type = CTy::Containers; + // TODO: ... } }; } // namespace froaring \ No newline at end of file diff --git a/lib/rle_container.h b/lib/rle_container.h index 00dde31..4825e9e 100644 --- a/lib/rle_container.h +++ b/lib/rle_container.h @@ -13,85 +13,89 @@ namespace froaring { template class RLEContainer { - using DataType = froaring::can_fit_t; - static constexpr DataType initial_capacity = RLE_CONTAINER_INIT_SIZE; - using RunPair = std::pair; // e.g., {5,10} stands for + using IndexOrNumType = froaring::can_fit_t; + using SizeType = froaring::can_fit_t; + static constexpr SizeType initial_capacity = RLE_CONTAINER_INIT_SIZE; + using RunPair = + std::pair; // e.g., {5,10} stands for // [5,6,7,8,9,10] - RunPair* runs; - size_t capacity; - DataType run_count; // Always less than 2**(DataBits-1) + SizeType capacity; + IndexOrNumType size; // Always less than 2**(DataBits-1) + RunPair runs[]; public: - RLEContainer() - : runs(new RunPair[initial_capacity]), - capacity(initial_capacity), - run_count(0) {} - - ~RLEContainer() { delete[] runs; } - RLEContainer(const RLEContainer&) = delete; RLEContainer& operator=(const RLEContainer&) = delete; - void clear() { run_count = 0; } + static RLEContainer* create(SizeType capacity = RLE_CONTAINER_INIT_SIZE) { + size_t totalSize = sizeof(RLEContainer) + capacity * sizeof(RunPair); + void* memory = operator new(totalSize); + RLEContainer* container = new (memory) RLEContainer(); + return container; + } + + void clear() { size = 0; } - void set(DataType num) { - if (!run_count) { - assert(capacity > 0 && "???"); - runs[0] = {num, num}; - run_count = 1; + /// @brief Set a bit of the container. NOTE: `c` may be changed! + /// @param c Points to the container, may be changed if the container get + /// expanded. + /// @param num The bit to set. + static void set(RLEContainer*& c, IndexOrNumType num) { + if (!c->size) { + c->runs[0] = {num, num}; + c->size = 1; return; } - auto pos = upper_bound(num); - if (pos < run_count && runs[pos].first <= num && - num <= runs[pos].second) { + auto pos = c->upper_bound(num); + if (pos < c->size && c->runs[pos].first <= num && + num <= c->runs[pos].second) { return; // already set, do nothing. } - set_raw(pos, num); + set_raw(c, pos, num); } - void reset(DataType num) { - if (!run_count) return; - auto pos = upper_bound(num); - auto& run = runs[pos]; - if (run.first > num || run.second < num) return; - - if (run.first == num && - run.second == num) { // run is a single element, just remove it - memmove(&runs[pos], &runs[pos + 1], - (run_count - pos - 1) * sizeof(RunPair)); - --run_count; - } else if (run.first == num) { - run.first++; - } else if (run.second == num) { - run.second--; + static void reset(RLEContainer*& c, IndexOrNumType num) { + if (!c->size) return; + auto pos = c->upper_bound(num); + auto old_end = c->runs[pos].second; + if (c->runs[pos].first > num || c->runs[pos].second < num) return; + + if (c->runs[pos].first == num && + c->runs[pos].second == + num) { // run is a single element, just remove it + memmove(&c->runs[pos], &c->runs[pos + 1], + (c->size - pos - 1) * sizeof(RunPair)); + --c->size; + } else if (c->runs[pos].first == num) { + c->runs[pos].first++; + } else if (c->runs[pos].second == num) { + c->runs[pos].second--; } else { // split the run [a,b] into: [a, num-1] and [num+1, b] - if (run_count == capacity) { - expand(); - } - auto old_end = run.second; // b - std::memmove(&runs[pos + 2], &runs[pos + 1], - (run_count - pos - 1) * sizeof(RunPair)); - run_count++; - runs[pos].second = num - 1; - runs[pos + 1] = {num + 1, old_end}; + if (c->size == c->capacity) RLEContainer::expand(c); + + std::memmove(&c->runs[pos + 2], &c->runs[pos + 1], + (c->size - pos - 1) * sizeof(RunPair)); + c->size++; + c->runs[pos].second = num - 1; + c->runs[pos + 1] = {num + 1, old_end}; } } - bool test(DataType num) const { - if (!run_count) return false; + bool test(IndexOrNumType num) const { + if (!size) return false; auto pos = upper_bound(num); - return (pos < run_count && runs[pos].first <= num && + return (pos < size && runs[pos].first <= num && num <= runs[pos].second); } - bool test_and_set(DataType num) { + bool test_and_set(IndexOrNumType num) { bool was_set; - DataType pos; - if (!run_count) { + IndexOrNumType pos; + if (!size) { was_set = false; } else { pos = upper_bound(num); - was_set = (pos < run_count && runs[pos].first <= num && + was_set = (pos < size && runs[pos].first <= num && num <= runs[pos].second); } if (was_set) return false; @@ -99,19 +103,19 @@ class RLEContainer { return true; } - size_t cardinality() const { - size_t count = 0; - for (DataType i = 0; i < run_count; ++i) { + SizeType cardinality() const { + SizeType count = 0; + for (IndexOrNumType i = 0; i < size; ++i) { count += runs[i].second - runs[i].first; } // We need to add 1 to the count because the range is inclusive - return count + run_count; + return count + size; } - DataType pairs() const { return run_count; } + IndexOrNumType pairs() const { return size; } void debug_print() const { - for (size_t i = 0; i < run_count; ++i) { + for (size_t i = 0; i < size; ++i) { std::cout << "[" << int(runs[i].first) << "," << int(runs[i].second) << "] "; } @@ -119,12 +123,15 @@ class RLEContainer { } private: - DataType upper_bound(DataType num) const { - assert(run_count && "Cannot find upper bound in an empty container"); - DataType low = 0; - DataType high = run_count; + RLEContainer(SizeType capacity = RLE_CONTAINER_INIT_SIZE, SizeType size = 0) + : capacity(capacity), size(size) {} + + SizeType upper_bound(IndexOrNumType num) const { + assert(size && "Cannot find upper bound in an empty container"); + SizeType low = 0; + SizeType high = size; while (low < high) { - DataType mid = low + (high - low) / 2; + SizeType mid = low + (high - low) / 2; if (runs[mid].first <= num) { low = mid + 1; } else { @@ -134,45 +141,53 @@ class RLEContainer { return (low > 0) ? low - 1 : 0; } - void expand() { - capacity *= 2; - auto new_runs = new RunPair[capacity]; - std::memmove(new_runs, runs, run_count * sizeof(RunPair)); - delete[] runs; - runs = new_runs; + static void expand(RLEContainer*& c) { + auto new_cap = c->capacity * 2; + + size_t totalSize = sizeof(RLEContainer) + new_cap * sizeof(RunPair); + void* new_memory = operator new(totalSize); + RLEContainer* new_container = + new (new_memory) RLEContainer(new_cap, c->size); + + std::memmove(&new_container->runs, &c->runs, c->size * sizeof(RunPair)); + c->~RLEContainer(); + operator delete(c); + + c = new_container; } - void set_raw(DataType pos, DataType num) { + static void set_raw(RLEContainer*& c, IndexOrNumType pos, + IndexOrNumType num) { // If the value is next to the previous run's end (and need merging) - bool merge_prev = (num > 0 && num - 1 == runs[pos].second); + bool merge_prev = (num > 0 && num - 1 == c->runs[pos].second); // If the value is next to the next run's start (and need merging) - bool merge_next = (pos < run_count - 1 && runs[pos + 1].first > 0 && - runs[pos + 1].first - 1 == num); + bool merge_next = (pos < c->size - 1 && c->runs[pos + 1].first > 0 && + c->runs[pos + 1].first - 1 == num); if (merge_prev && merge_next) { // [a,num-1] + num + [num+1, b] - runs[pos].second = runs[pos + 1].second; - std::memmove(&runs[pos + 1], &runs[pos + 2], - (run_count - pos - 1) * sizeof(RunPair)); - run_count--; + c->runs[pos].second = c->runs[pos + 1].second; + std::memmove(&c->runs[pos + 1], &c->runs[pos + 2], + (c->size - pos - 1) * sizeof(RunPair)); + c->size--; return; } if (merge_prev) { // [a,num-1] + num - runs[pos].second++; + c->runs[pos].second++; return; } if (merge_next) { // num + [num+1, b] - runs[pos + 1].first--; + c->runs[pos + 1].first--; return; } - if (run_count == capacity) { - expand(); + if (c->size == c->capacity) { + expand(c); } - if (run_count - pos > 2) - std::memmove(&runs[pos + 2], &runs[pos + 1], - (run_count - pos - 2) * sizeof(RunPair)); - run_count++; - runs[pos + 1] = {num, num}; + if (c->size - pos > 2) + std::memmove(&c->runs[pos + 2], &c->runs[pos + 1], + (c->size - pos - 2) * sizeof(RunPair)); + c->size++; + c->runs[pos + 1] = {num, num}; return; } }; diff --git a/tests/test_array_container.cpp b/tests/test_array_container.cpp index d3a37f7..e522e79 100644 --- a/tests/test_array_container.cpp +++ b/tests/test_array_container.cpp @@ -6,62 +6,73 @@ namespace froaring { class ArrayContainerTest : public ::testing::Test { protected: - ArrayContainer container; + using ArrayContainerSized = ArrayContainer; + ArrayContainerSized* container; void SetUp() override { - container.clear(); - container.set(1); - container.set(2); - container.set(3); + container = ArrayContainerSized::create(); + container->clear(); + ArrayContainerSized::set(container, 1); + ArrayContainerSized::set(container, 2); + ArrayContainerSized::set(container, 3); } }; TEST_F(ArrayContainerTest, SetBoundaryTest) { - container.set(0); - EXPECT_TRUE(container.test(0)); - EXPECT_EQ(container.cardinality(), 4); + ArrayContainerSized::set(container, 0); + EXPECT_TRUE(container->test(0)); + EXPECT_EQ(container->cardinality(), 4); - container.set(255); - EXPECT_TRUE(container.test(255)); - EXPECT_EQ(container.cardinality(), 5); + ArrayContainerSized::set(container, 255); + EXPECT_TRUE(container->test(255)); + EXPECT_EQ(container->cardinality(), 5); } TEST_F(ArrayContainerTest, ResetBoundaryTest) { - container.set(0); - container.set(255); + ArrayContainerSized::set(container, 0); + ArrayContainerSized::set(container, 255); - container.reset(0); - EXPECT_FALSE(container.test(0)); - EXPECT_EQ(container.cardinality(), 4); + container->reset(0); + EXPECT_FALSE(container->test(0)); + EXPECT_EQ(container->cardinality(), 4); - container.reset(255); - EXPECT_FALSE(container.test(255)); - EXPECT_EQ(container.cardinality(), 3); + container->reset(255); + EXPECT_FALSE(container->test(255)); + EXPECT_EQ(container->cardinality(), 3); } TEST_F(ArrayContainerTest, TestBoundaryTest) { - container.set(0); - container.set(255); + ArrayContainerSized::set(container, 0); + ArrayContainerSized::set(container, 255); - EXPECT_TRUE(container.test(0)); + EXPECT_TRUE(container->test(0)); EXPECT_TRUE(255); - EXPECT_EQ(container.cardinality(), 5); + EXPECT_EQ(container->cardinality(), 5); } TEST_F(ArrayContainerTest, ExpandBoundaryTest) { for (uint64_t i = 4; i <= 10; ++i) { - container.set(i); + ArrayContainerSized::set(container, i); } - EXPECT_EQ(container.cardinality(), 10); - EXPECT_TRUE(container.test(10)); + EXPECT_EQ(container->cardinality(), 10); + EXPECT_TRUE(container->test(10)); - container.set(255); - EXPECT_EQ(container.cardinality(), 11); + ArrayContainerSized::set(container, 255); + EXPECT_EQ(container->cardinality(), 11); +} + +TEST_F(ArrayContainerTest, ExpansionTest) { + for (uint64_t i = 0; i <= 255; ++i) { + ArrayContainerSized::set(container, i); + } + container->debug_print(); + EXPECT_EQ(container->cardinality(), 256); + EXPECT_TRUE(container->test(255)); } } // namespace froaring -int main(int argc, char **argv) { +int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } \ No newline at end of file diff --git a/tests/test_bitmap_container.cpp b/tests/test_bitmap_container.cpp index 2219129..9678279 100644 --- a/tests/test_bitmap_container.cpp +++ b/tests/test_bitmap_container.cpp @@ -5,45 +5,47 @@ namespace froaring { class BitmapContainerTest : public ::testing::Test { protected: - BitmapContainer container; + BitmapContainer* container; - void SetUp() override { container.clear(); } + void SetUp() override { + container = BitmapContainer::create(); + } }; TEST_F(BitmapContainerTest, TestSetAndTest) { - container.set(5); - EXPECT_TRUE(container.test(5)); - EXPECT_FALSE(container.test(10)); + container->set(5); + EXPECT_TRUE(container->test(5)); + EXPECT_FALSE(container->test(10)); } TEST_F(BitmapContainerTest, TestReset) { - container.set(5); - container.reset(5); - EXPECT_FALSE(container.test(5)); + container->set(5); + container->reset(5); + EXPECT_FALSE(container->test(5)); } TEST_F(BitmapContainerTest, TestCardinality) { - container.set(5); - container.set(10); - container.set(11); - container.set(11); - container.set(12); - container.set(255); - container.set(254); - - EXPECT_EQ(container.cardinality(), 6); - container.reset(11); - EXPECT_EQ(container.cardinality(), 5); + container->set(5); + container->set(10); + container->set(11); + container->set(11); + container->set(12); + container->set(255); + container->set(254); + + EXPECT_EQ(container->cardinality(), 6); + container->reset(11); + EXPECT_EQ(container->cardinality(), 5); } TEST_F(BitmapContainerTest, TestTestAndSet) { - EXPECT_TRUE(container.test_and_set(255)); - EXPECT_TRUE(container.test(255)); - EXPECT_FALSE(container.test_and_set(255)); + EXPECT_TRUE(container->test_and_set(255)); + EXPECT_TRUE(container->test(255)); + EXPECT_FALSE(container->test_and_set(255)); } } // namespace froaring -int main(int argc, char **argv) { +int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } \ No newline at end of file diff --git a/tests/test_rle_container.cpp b/tests/test_rle_container.cpp index f4fbe6f..a1dec36 100644 --- a/tests/test_rle_container.cpp +++ b/tests/test_rle_container.cpp @@ -6,82 +6,96 @@ namespace froaring { class RLEContainerTest : public ::testing::Test { protected: - RLEContainer container; + using RLESized = RLEContainer; + RLEContainer* container; - void SetUp() override { container.clear(); } + void SetUp() override { container = RLEContainer::create(); } }; TEST_F(RLEContainerTest, SetSingleValue) { - container.set(5); - EXPECT_TRUE(container.test(5)); - EXPECT_EQ(container.cardinality(), 1); + RLESized::set(container, 5); + EXPECT_TRUE(container->test(5)); + EXPECT_EQ(container->cardinality(), 1); } TEST_F(RLEContainerTest, SetMultipleValues) { - container.set(5); - container.set(10); - container.set(15); - EXPECT_TRUE(container.test(5)); - EXPECT_TRUE(container.test(10)); - EXPECT_TRUE(container.test(15)); - EXPECT_EQ(container.cardinality(), 3); + RLESized::set(container, 5); + RLESized::set(container, 10); + RLESized::set(container, 15); + EXPECT_TRUE(container->test(5)); + EXPECT_TRUE(container->test(10)); + EXPECT_TRUE(container->test(15)); + EXPECT_EQ(container->cardinality(), 3); } TEST_F(RLEContainerTest, SetAndMergeRuns) { - container.set(5); - container.set(6); - container.set(7); - EXPECT_TRUE(container.test(5)); - EXPECT_TRUE(container.test(6)); - EXPECT_TRUE(container.test(7)); - EXPECT_EQ(container.cardinality(), 3); + RLESized::set(container, 5); + RLESized::set(container, 6); + RLESized::set(container, 7); + EXPECT_TRUE(container->test(5)); + EXPECT_TRUE(container->test(6)); + EXPECT_TRUE(container->test(7)); + EXPECT_EQ(container->cardinality(), 3); } TEST_F(RLEContainerTest, ResetSingleValue) { - container.set(5); - container.reset(5); - EXPECT_FALSE(container.test(5)); - EXPECT_EQ(container.cardinality(), 0); + RLESized::set(container, 5); + RLESized::reset(container, 5); + EXPECT_FALSE(container->test(5)); + EXPECT_EQ(container->cardinality(), 0); } TEST_F(RLEContainerTest, ResetValueInRun) { - container.set(5); - container.set(6); - container.set(7); - container.set(7); - container.set(8); - container.set(9); - - container.set(255); - container.set(254); - container.set(253); - container.set(252); - container.set(251); - - container.reset(7); - container.reset(253); - - EXPECT_EQ(container.pairs(), 4); - EXPECT_TRUE(container.test(5)); - EXPECT_TRUE(container.test(6)); - EXPECT_FALSE(container.test(7)); - EXPECT_EQ(container.cardinality(), 8); + RLESized::set(container, 5); + RLESized::set(container, 6); + RLESized::set(container, 7); + RLESized::set(container, 7); + RLESized::set(container, 8); + RLESized::set(container, 9); + + RLESized::set(container, 255); + RLESized::set(container, 254); + RLESized::set(container, 253); + RLESized::set(container, 252); + RLESized::set(container, 251); + + RLESized::reset(container, 7); + RLESized::reset(container, 253); + + EXPECT_EQ(container->pairs(), 4); + EXPECT_TRUE(container->test(5)); + EXPECT_TRUE(container->test(6)); + EXPECT_FALSE(container->test(7)); + EXPECT_EQ(container->cardinality(), 8); } TEST_F(RLEContainerTest, ResetAndSplitRun) { - container.set(5); - container.set(6); - container.set(7); - container.reset(6); - EXPECT_TRUE(container.test(5)); - EXPECT_FALSE(container.test(6)); - EXPECT_TRUE(container.test(7)); - EXPECT_EQ(container.cardinality(), 2); + RLESized::set(container, 5); + RLESized::set(container, 6); + RLESized::set(container, 7); + RLESized::reset(container, 6); + EXPECT_TRUE(container->test(5)); + EXPECT_FALSE(container->test(6)); + EXPECT_TRUE(container->test(7)); + EXPECT_EQ(container->cardinality(), 2); +} + +TEST_F(RLEContainerTest, AlternativelySetTest) { + for (size_t i = 0; i < 256; i += 2) { + RLESized::set(container, i); + } + EXPECT_EQ(container->cardinality(), 128); + EXPECT_EQ(container->pairs(), 128); + for (size_t i = 1; i < 256; i += 2) { + RLESized::set(container, i); + } + EXPECT_EQ(container->cardinality(), 256); + EXPECT_EQ(container->pairs(), 1); } } // namespace froaring -int main(int argc, char **argv) { +int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } \ No newline at end of file