Skip to content

Commit eb27256

Browse files
[libc] Add erase function to blockstore (#98674)
Reland of #97641 with sanitizer fixes This adds the ability to erase a value from a blockstore based on an iterator. For usability/testing purposes it also includes an addition operator for blockstore's iterator.
1 parent 9bd575d commit eb27256

File tree

2 files changed

+153
-2
lines changed

2 files changed

+153
-2
lines changed

libc/src/__support/blockstore.h

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99
#ifndef LLVM_LIBC_SRC___SUPPORT_BLOCKSTORE_H
1010
#define LLVM_LIBC_SRC___SUPPORT_BLOCKSTORE_H
1111

12+
#include "src/__support/CPP/array.h"
13+
#include "src/__support/CPP/new.h"
14+
#include "src/__support/CPP/type_traits.h"
15+
#include "src/__support/libc_assert.h"
1216
#include "src/__support/macros/config.h"
13-
#include <src/__support/CPP/new.h>
14-
#include <src/__support/libc_assert.h>
1517

1618
#include <stddef.h>
1719
#include <stdint.h>
@@ -98,6 +100,16 @@ class BlockStore {
98100
return *reinterpret_cast<T *>(block->data + sizeof(T) * true_index);
99101
}
100102

103+
LIBC_INLINE Iterator operator+(int i) {
104+
LIBC_ASSERT(i >= 0 &&
105+
"BlockStore iterators only support incrementation.");
106+
auto other = *this;
107+
for (int j = 0; j < i; ++j)
108+
++other;
109+
110+
return other;
111+
}
112+
101113
LIBC_INLINE bool operator==(const Iterator &rhs) const {
102114
return block == rhs.block && index == rhs.index;
103115
}
@@ -176,6 +188,47 @@ class BlockStore {
176188
else
177189
return Iterator(current, fill_count);
178190
}
191+
192+
// Removes the element at pos, then moves all the objects after back by one to
193+
// fill the hole. It's assumed that pos is a valid iterator to somewhere in
194+
// this block_store.
195+
LIBC_INLINE void erase(Iterator pos) {
196+
const Iterator last_item = Iterator(current, fill_count);
197+
if (pos == last_item) {
198+
pop_back();
199+
return;
200+
}
201+
202+
if constexpr (REVERSE_ORDER) {
203+
// REVERSE: Iterate from begin to pos
204+
const Iterator range_end = pos;
205+
Iterator cur = begin();
206+
T prev_val = *cur;
207+
++cur;
208+
T cur_val = *cur;
209+
210+
for (; cur != range_end; ++cur) {
211+
cur_val = *cur;
212+
*cur = prev_val;
213+
prev_val = cur_val;
214+
}
215+
// As long as this isn't the end we will always need to move at least one
216+
// item (since we know that pos isn't the last item due to the check
217+
// above).
218+
if (range_end != end())
219+
*cur = prev_val;
220+
} else {
221+
// FORWARD: Iterate from pos to end
222+
const Iterator range_end = end();
223+
Iterator cur = pos;
224+
Iterator prev = cur;
225+
++cur;
226+
227+
for (; cur != range_end; prev = cur, ++cur)
228+
*prev = *cur;
229+
}
230+
pop_back();
231+
}
179232
};
180233

181234
template <typename T, size_t BLOCK_SIZE, bool REVERSE_ORDER>

libc/test/src/__support/blockstore_test.cpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,99 @@ class LlvmLibcBlockStoreTest : public LIBC_NAMESPACE::testing::Test {
6464
}
6565
block_store.destroy(&block_store);
6666
}
67+
68+
template <bool REVERSE> void erase_test() {
69+
using LIBC_NAMESPACE::BlockStore;
70+
BlockStore<int, 2, REVERSE> block_store;
71+
int i;
72+
73+
constexpr int ARR_SIZE = 6;
74+
75+
ASSERT_TRUE(block_store.empty());
76+
for (int i = 0; i < ARR_SIZE; i++) {
77+
ASSERT_TRUE(block_store.push_back(i + 1));
78+
}
79+
80+
// block_store state should be {1,2,3,4,5,6}
81+
82+
block_store.erase(block_store.begin());
83+
84+
// FORWARD: block_store state should be {2,3,4,5,6}
85+
// REVERSE: block_store state should be {1,2,3,4,5}
86+
87+
auto iter = block_store.begin();
88+
for (i = 0; iter != block_store.end(); ++i, ++iter) {
89+
if (!REVERSE) {
90+
ASSERT_EQ(*iter, i + 2);
91+
} else {
92+
ASSERT_EQ(*iter, (ARR_SIZE - 1) - i);
93+
}
94+
}
95+
96+
// Assert that there were the correct number of elements
97+
ASSERT_EQ(i, ARR_SIZE - 1);
98+
99+
block_store.erase(block_store.end());
100+
101+
// BOTH: block_store state should be {2,3,4,5}
102+
103+
iter = block_store.begin();
104+
for (i = 0; iter != block_store.end(); ++i, ++iter) {
105+
if (!REVERSE) {
106+
ASSERT_EQ(*iter, i + 2);
107+
} else {
108+
ASSERT_EQ(*iter, (ARR_SIZE - 1) - i);
109+
}
110+
}
111+
112+
ASSERT_EQ(i, ARR_SIZE - 2);
113+
114+
block_store.erase(block_store.begin() + 1);
115+
116+
// FORWARD: block_store state should be {2,4,5}
117+
// REVERSE: block_store state should be {2,3,5}
118+
119+
const int FORWARD_RESULTS[] = {2, 4, 5};
120+
const int REVERSE_RESULTS[] = {2, 3, 5};
121+
122+
iter = block_store.begin();
123+
for (i = 0; iter != block_store.end(); ++i, ++iter) {
124+
if (!REVERSE) {
125+
ASSERT_EQ(*iter, FORWARD_RESULTS[i]);
126+
} else {
127+
ASSERT_EQ(*iter, REVERSE_RESULTS[ARR_SIZE - 4 - i]); // reversed
128+
}
129+
}
130+
131+
ASSERT_EQ(i, ARR_SIZE - 3);
132+
133+
block_store.erase(block_store.begin() + 1);
134+
// BOTH: block_store state should be {2,5}
135+
136+
iter = block_store.begin();
137+
if (!REVERSE) {
138+
ASSERT_EQ(*iter, 2);
139+
ASSERT_EQ(*(iter + 1), 5);
140+
} else {
141+
ASSERT_EQ(*iter, 5);
142+
ASSERT_EQ(*(iter + 1), 2);
143+
}
144+
145+
block_store.erase(block_store.begin());
146+
// FORWARD: block_store state should be {5}
147+
// REVERSE: block_store state should be {2}
148+
iter = block_store.begin();
149+
if (!REVERSE) {
150+
ASSERT_EQ(*iter, 5);
151+
} else {
152+
ASSERT_EQ(*iter, 2);
153+
}
154+
155+
block_store.erase(block_store.begin());
156+
// BOTH: block_store state should be {}
157+
158+
block_store.destroy(&block_store);
159+
}
67160
};
68161

69162
TEST_F(LlvmLibcBlockStoreTest, PopulateAndIterate4) {
@@ -100,3 +193,8 @@ TEST_F(LlvmLibcBlockStoreTest, Empty) {
100193
empty_test<false>();
101194
empty_test<true>();
102195
}
196+
197+
TEST_F(LlvmLibcBlockStoreTest, Erase) {
198+
erase_test<false>();
199+
erase_test<true>();
200+
}

0 commit comments

Comments
 (0)