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