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 //===----------------------------------------------------------------------===//
8
9 #ifndef LLVM_LIBC_SUPPORT_CPP_BLOCKSTORE_H
10 #define LLVM_LIBC_SUPPORT_CPP_BLOCKSTORE_H
11
12 #include <stddef.h>
13 #include <stdint.h>
14 #include <stdlib.h>
15
16 // TODO: fix our assert.h to make it useable
17 #define assert(x) \
18 if (!(x)) \
19 __builtin_trap()
20
21 namespace __llvm_libc {
22 namespace cpp {
23
24 // The difference between BlockStore a traditional vector types is that,
25 // when more capacity is desired, a new block is added instead of allocating
26 // a larger sized array and copying over existing items to the new allocation.
27 // Also, the initial block does not need heap allocation. Hence, a BlockStore is
28 // suitable for global objects as it does not require explicit construction.
29 // Also, the destructor of this class does nothing, which eliminates the need
30 // for an atexit global object destruction. But, it also means that the global
31 // object should be explicitly cleaned up at the appropriate time.
32 //
33 // If REVERSE_ORDER is true, the iteration of elements will in the reverse
34 // order. Also, since REVERSE_ORDER is a constexpr, conditionals branching
35 // on its value will be optimized out in the code below.
36 template <typename T, size_t BLOCK_SIZE, bool REVERSE_ORDER = false>
37 class BlockStore {
38 protected:
39 struct Block {
40 alignas(T) uint8_t data[BLOCK_SIZE * sizeof(T)] = {0};
41 Block *next = nullptr;
42 };
43
44 Block first;
45 Block *current = &first;
46 size_t fill_count = 0;
47
48 struct Pair {
49 Block *first, *second;
50 };
getLastBlocks()51 Pair getLastBlocks() {
52 if (REVERSE_ORDER)
53 return {current, current->next};
54 Block *prev = nullptr;
55 Block *curr = &first;
56 for (; curr->next; prev = curr, curr = curr->next)
57 ;
58 assert(curr == current);
59 return {curr, prev};
60 }
61
getLastBlock()62 Block *getLastBlock() { return getLastBlocks().first; }
63
64 public:
65 constexpr BlockStore() = default;
66 ~BlockStore() = default;
67
68 class iterator {
69 Block *block;
70 size_t index;
71
72 public:
iterator(Block * b,size_t i)73 constexpr iterator(Block *b, size_t i) : block(b), index(i) {}
74
75 iterator &operator++() {
76 if (REVERSE_ORDER) {
77 if (index == 0)
78 return *this;
79
80 --index;
81 if (index == 0 && block->next != nullptr) {
82 index = BLOCK_SIZE;
83 block = block->next;
84 }
85 } else {
86 if (index == BLOCK_SIZE)
87 return *this;
88
89 ++index;
90 if (index == BLOCK_SIZE && block->next != nullptr) {
91 index = 0;
92 block = block->next;
93 }
94 }
95
96 return *this;
97 }
98
99 T &operator*() {
100 size_t true_index = REVERSE_ORDER ? index - 1 : index;
101 return *reinterpret_cast<T *>(block->data + sizeof(T) * true_index);
102 }
103
104 bool operator==(const iterator &rhs) const {
105 return block == rhs.block && index == rhs.index;
106 }
107
108 bool operator!=(const iterator &rhs) const {
109 return block != rhs.block || index != rhs.index;
110 }
111 };
112
113 static void destroy(BlockStore<T, BLOCK_SIZE, REVERSE_ORDER> *block_store);
114
new_obj()115 T *new_obj() {
116 if (fill_count == BLOCK_SIZE) {
117 auto new_block = reinterpret_cast<Block *>(::malloc(sizeof(Block)));
118 if (REVERSE_ORDER) {
119 new_block->next = current;
120 } else {
121 new_block->next = nullptr;
122 current->next = new_block;
123 }
124 current = new_block;
125 fill_count = 0;
126 }
127 T *obj = reinterpret_cast<T *>(current->data + fill_count * sizeof(T));
128 ++fill_count;
129 return obj;
130 }
131
push_back(const T & value)132 void push_back(const T &value) {
133 T *ptr = new_obj();
134 *ptr = value;
135 }
136
back()137 T &back() {
138 return *reinterpret_cast<T *>(getLastBlock()->data +
139 sizeof(T) * (fill_count - 1));
140 }
141
pop_back()142 void pop_back() {
143 fill_count--;
144 if (fill_count || current == &first)
145 return;
146 auto [last, prev] = getLastBlocks();
147 if (REVERSE_ORDER) {
148 assert(last == current);
149 current = current->next;
150 } else {
151 assert(prev->next == last);
152 current = prev;
153 current->next = nullptr;
154 }
155 if (last != &first)
156 ::free(last);
157 fill_count = BLOCK_SIZE;
158 }
159
empty()160 bool empty() const { return current == &first && !fill_count; }
161
begin()162 iterator begin() {
163 if (REVERSE_ORDER)
164 return iterator(current, fill_count);
165 else
166 return iterator(&first, 0);
167 }
168
end()169 iterator end() {
170 if (REVERSE_ORDER)
171 return iterator(&first, 0);
172 else
173 return iterator(current, fill_count);
174 }
175 };
176
177 template <typename T, size_t BLOCK_SIZE, bool REVERSE_ORDER>
destroy(BlockStore<T,BLOCK_SIZE,REVERSE_ORDER> * block_store)178 void BlockStore<T, BLOCK_SIZE, REVERSE_ORDER>::destroy(
179 BlockStore<T, BLOCK_SIZE, REVERSE_ORDER> *block_store) {
180 if (REVERSE_ORDER) {
181 auto current = block_store->current;
182 while (current->next != nullptr) {
183 auto temp = current;
184 current = current->next;
185 free(temp);
186 }
187 } else {
188 auto current = block_store->first.next;
189 while (current != nullptr) {
190 auto temp = current;
191 current = current->next;
192 free(temp);
193 }
194 }
195 block_store->current = nullptr;
196 block_store->fill_count = 0;
197 }
198
199 // A convenience type for reverse order block stores.
200 template <typename T, size_t BLOCK_SIZE>
201 using ReverseOrderBlockStore = BlockStore<T, BLOCK_SIZE, true>;
202
203 } // namespace cpp
204 } // namespace __llvm_libc
205
206 #endif // LLVM_LIBC_SUPPORT_CPP_BLOCKSTORE_H
207