//===-- A data structure which stores data in blocks -----------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLVM_LIBC_SRC___SUPPORT_BLOCKSTORE_H #define LLVM_LIBC_SRC___SUPPORT_BLOCKSTORE_H #include "src/__support/CPP/array.h" #include "src/__support/CPP/new.h" #include "src/__support/CPP/type_traits.h" #include "src/__support/libc_assert.h" #include "src/__support/macros/config.h" #include #include namespace LIBC_NAMESPACE_DECL { // The difference between BlockStore a traditional vector types is that, // when more capacity is desired, a new block is added instead of allocating // a larger sized array and copying over existing items to the new allocation. // Also, the initial block does not need heap allocation. Hence, a BlockStore is // suitable for global objects as it does not require explicit construction. // Also, the destructor of this class does nothing, which eliminates the need // for an atexit global object destruction. But, it also means that the global // object should be explicitly cleaned up at the appropriate time. // // If REVERSE_ORDER is true, the iteration of elements will in the reverse // order. Also, since REVERSE_ORDER is a constexpr, conditionals branching // on its value will be optimized out in the code below. template class BlockStore { protected: struct Block { alignas(T) uint8_t data[BLOCK_SIZE * sizeof(T)] = {0}; Block *next = nullptr; }; Block first; Block *current = &first; size_t fill_count = 0; struct Pair { Block *first, *second; }; LIBC_INLINE Pair get_last_blocks() { if (REVERSE_ORDER) return {current, current->next}; Block *prev = nullptr; Block *curr = &first; for (; curr->next; prev = curr, curr = curr->next) ; LIBC_ASSERT(curr == current); return {curr, prev}; } LIBC_INLINE Block *get_last_block() { return get_last_blocks().first; } public: LIBC_INLINE constexpr BlockStore() = default; LIBC_INLINE ~BlockStore() = default; class Iterator { Block *block; size_t index; public: LIBC_INLINE constexpr Iterator(Block *b, size_t i) : block(b), index(i) {} LIBC_INLINE Iterator &operator++() { if (REVERSE_ORDER) { if (index == 0) return *this; --index; if (index == 0 && block->next != nullptr) { index = BLOCK_SIZE; block = block->next; } } else { if (index == BLOCK_SIZE) return *this; ++index; if (index == BLOCK_SIZE && block->next != nullptr) { index = 0; block = block->next; } } return *this; } LIBC_INLINE T &operator*() { size_t true_index = REVERSE_ORDER ? index - 1 : index; return *reinterpret_cast(block->data + sizeof(T) * true_index); } LIBC_INLINE Iterator operator+(int i) { LIBC_ASSERT(i >= 0 && "BlockStore iterators only support incrementation."); auto other = *this; for (int j = 0; j < i; ++j) ++other; return other; } LIBC_INLINE bool operator==(const Iterator &rhs) const { return block == rhs.block && index == rhs.index; } LIBC_INLINE bool operator!=(const Iterator &rhs) const { return block != rhs.block || index != rhs.index; } }; LIBC_INLINE static void destroy(BlockStore *block_store); LIBC_INLINE T *new_obj() { if (fill_count == BLOCK_SIZE) { AllocChecker ac; auto new_block = new (ac) Block(); if (!ac) return nullptr; if (REVERSE_ORDER) { new_block->next = current; } else { new_block->next = nullptr; current->next = new_block; } current = new_block; fill_count = 0; } T *obj = reinterpret_cast(current->data + fill_count * sizeof(T)); ++fill_count; return obj; } [[nodiscard]] LIBC_INLINE bool push_back(const T &value) { T *ptr = new_obj(); if (ptr == nullptr) return false; *ptr = value; return true; } LIBC_INLINE T &back() { return *reinterpret_cast(get_last_block()->data + sizeof(T) * (fill_count - 1)); } LIBC_INLINE void pop_back() { fill_count--; if (fill_count || current == &first) return; auto [last, prev] = get_last_blocks(); if (REVERSE_ORDER) { LIBC_ASSERT(last == current); current = current->next; } else { LIBC_ASSERT(prev->next == last); current = prev; current->next = nullptr; } if (last != &first) delete last; fill_count = BLOCK_SIZE; } LIBC_INLINE bool empty() const { return current == &first && !fill_count; } LIBC_INLINE Iterator begin() { if (REVERSE_ORDER) return Iterator(current, fill_count); else return Iterator(&first, 0); } LIBC_INLINE Iterator end() { if (REVERSE_ORDER) return Iterator(&first, 0); else return Iterator(current, fill_count); } // Removes the element at pos, then moves all the objects after back by one to // fill the hole. It's assumed that pos is a valid iterator to somewhere in // this block_store. LIBC_INLINE void erase(Iterator pos) { const Iterator last_item = Iterator(current, fill_count); if (pos == last_item) { pop_back(); return; } if constexpr (REVERSE_ORDER) { // REVERSE: Iterate from begin to pos const Iterator range_end = pos; Iterator cur = begin(); T prev_val = *cur; ++cur; T cur_val = *cur; for (; cur != range_end; ++cur) { cur_val = *cur; *cur = prev_val; prev_val = cur_val; } // As long as this isn't the end we will always need to move at least one // item (since we know that pos isn't the last item due to the check // above). if (range_end != end()) *cur = prev_val; } else { // FORWARD: Iterate from pos to end const Iterator range_end = end(); Iterator cur = pos; Iterator prev = cur; ++cur; for (; cur != range_end; prev = cur, ++cur) *prev = *cur; } pop_back(); } }; template LIBC_INLINE void BlockStore::destroy( BlockStore *block_store) { if (REVERSE_ORDER) { auto current = block_store->current; while (current->next != nullptr) { auto temp = current; current = current->next; delete temp; } } else { auto current = block_store->first.next; while (current != nullptr) { auto temp = current; current = current->next; delete temp; } } block_store->current = nullptr; block_store->fill_count = 0; } // A convenience type for reverse order block stores. template using ReverseOrderBlockStore = BlockStore; } // namespace LIBC_NAMESPACE_DECL #endif // LLVM_LIBC_SRC___SUPPORT_BLOCKSTORE_H