1 //===-- A data structure which stores data in blocks -----------*- C++ -*-===//
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
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"
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>
39 alignas(T
) uint8_t data
[BLOCK_SIZE
* sizeof(T
)] = {0};
40 Block
*next
= nullptr;
44 Block
*current
= &first
;
45 size_t fill_count
= 0;
48 Block
*first
, *second
;
50 LIBC_INLINE Pair
get_last_blocks() {
52 return {current
, current
->next
};
53 Block
*prev
= nullptr;
55 for (; curr
->next
; prev
= curr
, curr
= curr
->next
)
57 LIBC_ASSERT(curr
== current
);
61 LIBC_INLINE Block
*get_last_block() { return get_last_blocks().first
; }
64 LIBC_INLINE
constexpr BlockStore() = default;
65 LIBC_INLINE
~BlockStore() = default;
72 LIBC_INLINE
constexpr Iterator(Block
*b
, size_t i
) : block(b
), index(i
) {}
74 LIBC_INLINE Iterator
&operator++() {
80 if (index
== 0 && block
->next
!= nullptr) {
85 if (index
== BLOCK_SIZE
)
89 if (index
== BLOCK_SIZE
&& block
->next
!= nullptr) {
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.");
107 for (int j
= 0; j
< i
; ++j
)
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
) {
128 auto new_block
= new (ac
) Block();
132 new_block
->next
= current
;
134 new_block
->next
= nullptr;
135 current
->next
= new_block
;
140 T
*obj
= reinterpret_cast<T
*>(current
->data
+ fill_count
* sizeof(T
));
145 [[nodiscard
]] LIBC_INLINE
bool push_back(const T
&value
) {
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() {
160 if (fill_count
|| current
== &first
)
162 auto [last
, prev
] = get_last_blocks();
164 LIBC_ASSERT(last
== current
);
165 current
= current
->next
;
167 LIBC_ASSERT(prev
->next
== last
);
169 current
->next
= nullptr;
173 fill_count
= BLOCK_SIZE
;
176 LIBC_INLINE
bool empty() const { return current
== &first
&& !fill_count
; }
178 LIBC_INLINE Iterator
begin() {
180 return Iterator(current
, fill_count
);
182 return Iterator(&first
, 0);
185 LIBC_INLINE Iterator
end() {
187 return Iterator(&first
, 0);
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
195 LIBC_INLINE
void erase(Iterator pos
) {
196 const Iterator last_item
= Iterator(current
, fill_count
);
197 if (pos
== last_item
) {
202 if constexpr (REVERSE_ORDER
) {
203 // REVERSE: Iterate from begin to pos
204 const Iterator range_end
= pos
;
205 Iterator cur
= begin();
210 for (; cur
!= range_end
; ++cur
) {
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
218 if (range_end
!= end())
221 // FORWARD: Iterate from pos to end
222 const Iterator range_end
= end();
227 for (; cur
!= range_end
; prev
= cur
, ++cur
)
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
) {
238 auto current
= block_store
->current
;
239 while (current
->next
!= nullptr) {
241 current
= current
->next
;
245 auto current
= block_store
->first
.next
;
246 while (current
!= nullptr) {
248 current
= current
->next
;
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