[libc++][Android] Allow testing libc++ with clang-r536225 (#116149)
[llvm-project.git] / libc / src / __support / blockstore.h
blobefe2234eace5967d97d1ffe5d3611dfe0500b097
1 //===-- A data structure which stores data in blocks -----------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_LIBC_SRC___SUPPORT_BLOCKSTORE_H
10 #define LLVM_LIBC_SRC___SUPPORT_BLOCKSTORE_H
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"
16 #include "src/__support/macros/config.h"
18 #include <stddef.h>
19 #include <stdint.h>
21 namespace LIBC_NAMESPACE_DECL {
23 // The difference between BlockStore a traditional vector types is that,
24 // when more capacity is desired, a new block is added instead of allocating
25 // a larger sized array and copying over existing items to the new allocation.
26 // Also, the initial block does not need heap allocation. Hence, a BlockStore is
27 // suitable for global objects as it does not require explicit construction.
28 // Also, the destructor of this class does nothing, which eliminates the need
29 // for an atexit global object destruction. But, it also means that the global
30 // object should be explicitly cleaned up at the appropriate time.
32 // If REVERSE_ORDER is true, the iteration of elements will in the reverse
33 // order. Also, since REVERSE_ORDER is a constexpr, conditionals branching
34 // on its value will be optimized out in the code below.
35 template <typename T, size_t BLOCK_SIZE, bool REVERSE_ORDER = false>
36 class BlockStore {
37 protected:
38 struct Block {
39 alignas(T) uint8_t data[BLOCK_SIZE * sizeof(T)] = {0};
40 Block *next = nullptr;
43 Block first;
44 Block *current = &first;
45 size_t fill_count = 0;
47 struct Pair {
48 Block *first, *second;
50 LIBC_INLINE Pair get_last_blocks() {
51 if (REVERSE_ORDER)
52 return {current, current->next};
53 Block *prev = nullptr;
54 Block *curr = &first;
55 for (; curr->next; prev = curr, curr = curr->next)
57 LIBC_ASSERT(curr == current);
58 return {curr, prev};
61 LIBC_INLINE Block *get_last_block() { return get_last_blocks().first; }
63 public:
64 LIBC_INLINE constexpr BlockStore() = default;
65 LIBC_INLINE ~BlockStore() = default;
67 class Iterator {
68 Block *block;
69 size_t index;
71 public:
72 LIBC_INLINE constexpr Iterator(Block *b, size_t i) : block(b), index(i) {}
74 LIBC_INLINE Iterator &operator++() {
75 if (REVERSE_ORDER) {
76 if (index == 0)
77 return *this;
79 --index;
80 if (index == 0 && block->next != nullptr) {
81 index = BLOCK_SIZE;
82 block = block->next;
84 } else {
85 if (index == BLOCK_SIZE)
86 return *this;
88 ++index;
89 if (index == BLOCK_SIZE && block->next != nullptr) {
90 index = 0;
91 block = block->next;
95 return *this;
98 LIBC_INLINE T &operator*() {
99 size_t true_index = REVERSE_ORDER ? index - 1 : index;
100 return *reinterpret_cast<T *>(block->data + sizeof(T) * true_index);
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;
110 return other;
113 LIBC_INLINE bool operator==(const Iterator &rhs) const {
114 return block == rhs.block && index == rhs.index;
117 LIBC_INLINE bool operator!=(const Iterator &rhs) const {
118 return block != rhs.block || index != rhs.index;
122 LIBC_INLINE static void
123 destroy(BlockStore<T, BLOCK_SIZE, REVERSE_ORDER> *block_store);
125 LIBC_INLINE T *new_obj() {
126 if (fill_count == BLOCK_SIZE) {
127 AllocChecker ac;
128 auto new_block = new (ac) Block();
129 if (!ac)
130 return nullptr;
131 if (REVERSE_ORDER) {
132 new_block->next = current;
133 } else {
134 new_block->next = nullptr;
135 current->next = new_block;
137 current = new_block;
138 fill_count = 0;
140 T *obj = reinterpret_cast<T *>(current->data + fill_count * sizeof(T));
141 ++fill_count;
142 return obj;
145 [[nodiscard]] LIBC_INLINE bool push_back(const T &value) {
146 T *ptr = new_obj();
147 if (ptr == nullptr)
148 return false;
149 *ptr = value;
150 return true;
153 LIBC_INLINE T &back() {
154 return *reinterpret_cast<T *>(get_last_block()->data +
155 sizeof(T) * (fill_count - 1));
158 LIBC_INLINE void pop_back() {
159 fill_count--;
160 if (fill_count || current == &first)
161 return;
162 auto [last, prev] = get_last_blocks();
163 if (REVERSE_ORDER) {
164 LIBC_ASSERT(last == current);
165 current = current->next;
166 } else {
167 LIBC_ASSERT(prev->next == last);
168 current = prev;
169 current->next = nullptr;
171 if (last != &first)
172 delete last;
173 fill_count = BLOCK_SIZE;
176 LIBC_INLINE bool empty() const { return current == &first && !fill_count; }
178 LIBC_INLINE Iterator begin() {
179 if (REVERSE_ORDER)
180 return Iterator(current, fill_count);
181 else
182 return Iterator(&first, 0);
185 LIBC_INLINE Iterator end() {
186 if (REVERSE_ORDER)
187 return Iterator(&first, 0);
188 else
189 return Iterator(current, fill_count);
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;
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;
210 for (; cur != range_end; ++cur) {
211 cur_val = *cur;
212 *cur = prev_val;
213 prev_val = cur_val;
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;
227 for (; cur != range_end; prev = cur, ++cur)
228 *prev = *cur;
230 pop_back();
234 template <typename T, size_t BLOCK_SIZE, bool REVERSE_ORDER>
235 LIBC_INLINE void BlockStore<T, BLOCK_SIZE, REVERSE_ORDER>::destroy(
236 BlockStore<T, BLOCK_SIZE, REVERSE_ORDER> *block_store) {
237 if (REVERSE_ORDER) {
238 auto current = block_store->current;
239 while (current->next != nullptr) {
240 auto temp = current;
241 current = current->next;
242 delete temp;
244 } else {
245 auto current = block_store->first.next;
246 while (current != nullptr) {
247 auto temp = current;
248 current = current->next;
249 delete temp;
252 block_store->current = nullptr;
253 block_store->fill_count = 0;
256 // A convenience type for reverse order block stores.
257 template <typename T, size_t BLOCK_SIZE>
258 using ReverseOrderBlockStore = BlockStore<T, BLOCK_SIZE, true>;
260 } // namespace LIBC_NAMESPACE_DECL
262 #endif // LLVM_LIBC_SRC___SUPPORT_BLOCKSTORE_H