diff --git a/libc/src/__support/blockstore.h b/libc/src/__support/blockstore.h index 8d13e0ed290dfd..efe2234eace596 100644 --- a/libc/src/__support/blockstore.h +++ b/libc/src/__support/blockstore.h @@ -9,9 +9,11 @@ #ifndef LLVM_LIBC_SRC___SUPPORT_BLOCKSTORE_H #define LLVM_LIBC_SRC___SUPPORT_BLOCKSTORE_H +#include "src/__support/CPP/array.h" +#include "src/__support/CPP/new.h" +#include "src/__support/CPP/type_traits.h" +#include "src/__support/libc_assert.h" #include "src/__support/macros/config.h" -#include -#include #include #include @@ -98,6 +100,16 @@ class BlockStore { return *reinterpret_cast(block->data + sizeof(T) * true_index); } + LIBC_INLINE Iterator operator+(int i) { + LIBC_ASSERT(i >= 0 && + "BlockStore iterators only support incrementation."); + auto other = *this; + for (int j = 0; j < i; ++j) + ++other; + + return other; + } + LIBC_INLINE bool operator==(const Iterator &rhs) const { return block == rhs.block && index == rhs.index; } @@ -176,6 +188,47 @@ class BlockStore { else return Iterator(current, fill_count); } + + // Removes the element at pos, then moves all the objects after back by one to + // fill the hole. It's assumed that pos is a valid iterator to somewhere in + // this block_store. + LIBC_INLINE void erase(Iterator pos) { + const Iterator last_item = Iterator(current, fill_count); + if (pos == last_item) { + pop_back(); + return; + } + + if constexpr (REVERSE_ORDER) { + // REVERSE: Iterate from begin to pos + const Iterator range_end = pos; + Iterator cur = begin(); + T prev_val = *cur; + ++cur; + T cur_val = *cur; + + for (; cur != range_end; ++cur) { + cur_val = *cur; + *cur = prev_val; + prev_val = cur_val; + } + // As long as this isn't the end we will always need to move at least one + // item (since we know that pos isn't the last item due to the check + // above). + if (range_end != end()) + *cur = prev_val; + } else { + // FORWARD: Iterate from pos to end + const Iterator range_end = end(); + Iterator cur = pos; + Iterator prev = cur; + ++cur; + + for (; cur != range_end; prev = cur, ++cur) + *prev = *cur; + } + pop_back(); + } }; template diff --git a/libc/test/src/__support/blockstore_test.cpp b/libc/test/src/__support/blockstore_test.cpp index 5fe8fef1b6edcc..dd74ea18f2c02f 100644 --- a/libc/test/src/__support/blockstore_test.cpp +++ b/libc/test/src/__support/blockstore_test.cpp @@ -64,6 +64,99 @@ class LlvmLibcBlockStoreTest : public LIBC_NAMESPACE::testing::Test { } block_store.destroy(&block_store); } + + template void erase_test() { + using LIBC_NAMESPACE::BlockStore; + BlockStore block_store; + int i; + + constexpr int ARR_SIZE = 6; + + ASSERT_TRUE(block_store.empty()); + for (int i = 0; i < ARR_SIZE; i++) { + ASSERT_TRUE(block_store.push_back(i + 1)); + } + + // block_store state should be {1,2,3,4,5,6} + + block_store.erase(block_store.begin()); + + // FORWARD: block_store state should be {2,3,4,5,6} + // REVERSE: block_store state should be {1,2,3,4,5} + + auto iter = block_store.begin(); + for (i = 0; iter != block_store.end(); ++i, ++iter) { + if (!REVERSE) { + ASSERT_EQ(*iter, i + 2); + } else { + ASSERT_EQ(*iter, (ARR_SIZE - 1) - i); + } + } + + // Assert that there were the correct number of elements + ASSERT_EQ(i, ARR_SIZE - 1); + + block_store.erase(block_store.end()); + + // BOTH: block_store state should be {2,3,4,5} + + iter = block_store.begin(); + for (i = 0; iter != block_store.end(); ++i, ++iter) { + if (!REVERSE) { + ASSERT_EQ(*iter, i + 2); + } else { + ASSERT_EQ(*iter, (ARR_SIZE - 1) - i); + } + } + + ASSERT_EQ(i, ARR_SIZE - 2); + + block_store.erase(block_store.begin() + 1); + + // FORWARD: block_store state should be {2,4,5} + // REVERSE: block_store state should be {2,3,5} + + const int FORWARD_RESULTS[] = {2, 4, 5}; + const int REVERSE_RESULTS[] = {2, 3, 5}; + + iter = block_store.begin(); + for (i = 0; iter != block_store.end(); ++i, ++iter) { + if (!REVERSE) { + ASSERT_EQ(*iter, FORWARD_RESULTS[i]); + } else { + ASSERT_EQ(*iter, REVERSE_RESULTS[ARR_SIZE - 4 - i]); // reversed + } + } + + ASSERT_EQ(i, ARR_SIZE - 3); + + block_store.erase(block_store.begin() + 1); + // BOTH: block_store state should be {2,5} + + iter = block_store.begin(); + if (!REVERSE) { + ASSERT_EQ(*iter, 2); + ASSERT_EQ(*(iter + 1), 5); + } else { + ASSERT_EQ(*iter, 5); + ASSERT_EQ(*(iter + 1), 2); + } + + block_store.erase(block_store.begin()); + // FORWARD: block_store state should be {5} + // REVERSE: block_store state should be {2} + iter = block_store.begin(); + if (!REVERSE) { + ASSERT_EQ(*iter, 5); + } else { + ASSERT_EQ(*iter, 2); + } + + block_store.erase(block_store.begin()); + // BOTH: block_store state should be {} + + block_store.destroy(&block_store); + } }; TEST_F(LlvmLibcBlockStoreTest, PopulateAndIterate4) { @@ -100,3 +193,8 @@ TEST_F(LlvmLibcBlockStoreTest, Empty) { empty_test(); empty_test(); } + +TEST_F(LlvmLibcBlockStoreTest, Erase) { + erase_test(); + erase_test(); +}