Path: blob/main/contrib/llvm-project/libc/src/__support/blockstore.h
213766 views
//===-- A data structure which stores data in blocks -----------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#ifndef LLVM_LIBC_SRC___SUPPORT_BLOCKSTORE_H9#define LLVM_LIBC_SRC___SUPPORT_BLOCKSTORE_H1011#include "src/__support/CPP/array.h"12#include "src/__support/CPP/new.h"13#include "src/__support/CPP/type_traits.h"14#include "src/__support/libc_assert.h"15#include "src/__support/macros/config.h"1617#include <stddef.h>18#include <stdint.h>1920namespace LIBC_NAMESPACE_DECL {2122// The difference between BlockStore a traditional vector types is that,23// when more capacity is desired, a new block is added instead of allocating24// a larger sized array and copying over existing items to the new allocation.25// Also, the initial block does not need heap allocation. Hence, a BlockStore is26// suitable for global objects as it does not require explicit construction.27// Also, the destructor of this class does nothing, which eliminates the need28// for an atexit global object destruction. But, it also means that the global29// object should be explicitly cleaned up at the appropriate time.30//31// If REVERSE_ORDER is true, the iteration of elements will in the reverse32// order. Also, since REVERSE_ORDER is a constexpr, conditionals branching33// on its value will be optimized out in the code below.34template <typename T, size_t BLOCK_SIZE, bool REVERSE_ORDER = false>35class BlockStore {36protected:37struct Block {38alignas(T) uint8_t data[BLOCK_SIZE * sizeof(T)] = {0};39Block *next = nullptr;40};4142Block first;43Block *current = &first;44size_t fill_count = 0;4546struct Pair {47Block *first, *second;48};49LIBC_INLINE Pair get_last_blocks() {50if (REVERSE_ORDER)51return {current, current->next};52Block *prev = nullptr;53Block *curr = &first;54for (; curr->next; prev = curr, curr = curr->next)55;56LIBC_ASSERT(curr == current);57return {curr, prev};58}5960LIBC_INLINE Block *get_last_block() { return get_last_blocks().first; }6162public:63LIBC_INLINE constexpr BlockStore() = default;64LIBC_INLINE ~BlockStore() = default;6566class Iterator {67Block *block;68size_t index;6970public:71LIBC_INLINE constexpr Iterator(Block *b, size_t i) : block(b), index(i) {}7273LIBC_INLINE Iterator &operator++() {74if (REVERSE_ORDER) {75if (index == 0)76return *this;7778--index;79if (index == 0 && block->next != nullptr) {80index = BLOCK_SIZE;81block = block->next;82}83} else {84if (index == BLOCK_SIZE)85return *this;8687++index;88if (index == BLOCK_SIZE && block->next != nullptr) {89index = 0;90block = block->next;91}92}9394return *this;95}9697LIBC_INLINE T &operator*() {98size_t true_index = REVERSE_ORDER ? index - 1 : index;99return *reinterpret_cast<T *>(block->data + sizeof(T) * true_index);100}101102LIBC_INLINE Iterator operator+(int i) {103LIBC_ASSERT(i >= 0 &&104"BlockStore iterators only support incrementation.");105auto other = *this;106for (int j = 0; j < i; ++j)107++other;108109return other;110}111112LIBC_INLINE bool operator==(const Iterator &rhs) const {113return block == rhs.block && index == rhs.index;114}115116LIBC_INLINE bool operator!=(const Iterator &rhs) const {117return block != rhs.block || index != rhs.index;118}119};120121LIBC_INLINE static void122destroy(BlockStore<T, BLOCK_SIZE, REVERSE_ORDER> *block_store);123124LIBC_INLINE T *new_obj() {125if (fill_count == BLOCK_SIZE) {126AllocChecker ac;127auto new_block = new (ac) Block();128if (!ac)129return nullptr;130if (REVERSE_ORDER) {131new_block->next = current;132} else {133new_block->next = nullptr;134current->next = new_block;135}136current = new_block;137fill_count = 0;138}139T *obj = reinterpret_cast<T *>(current->data + fill_count * sizeof(T));140++fill_count;141return obj;142}143144[[nodiscard]] LIBC_INLINE bool push_back(const T &value) {145T *ptr = new_obj();146if (ptr == nullptr)147return false;148*ptr = value;149return true;150}151152LIBC_INLINE T &back() {153return *reinterpret_cast<T *>(get_last_block()->data +154sizeof(T) * (fill_count - 1));155}156157LIBC_INLINE void pop_back() {158fill_count--;159if (fill_count || current == &first)160return;161auto [last, prev] = get_last_blocks();162if (REVERSE_ORDER) {163LIBC_ASSERT(last == current);164current = current->next;165} else {166LIBC_ASSERT(prev->next == last);167current = prev;168current->next = nullptr;169}170if (last != &first)171delete last;172fill_count = BLOCK_SIZE;173}174175LIBC_INLINE bool empty() const { return current == &first && !fill_count; }176177LIBC_INLINE Iterator begin() {178if (REVERSE_ORDER)179return Iterator(current, fill_count);180else181return Iterator(&first, 0);182}183184LIBC_INLINE Iterator end() {185if (REVERSE_ORDER)186return Iterator(&first, 0);187else188return Iterator(current, fill_count);189}190191// Removes the element at pos, then moves all the objects after back by one to192// fill the hole. It's assumed that pos is a valid iterator to somewhere in193// this block_store.194LIBC_INLINE void erase(Iterator pos) {195const Iterator last_item = Iterator(current, fill_count);196if (pos == last_item) {197pop_back();198return;199}200201if constexpr (REVERSE_ORDER) {202// REVERSE: Iterate from begin to pos203const Iterator range_end = pos;204Iterator cur = begin();205T prev_val = *cur;206++cur;207T cur_val = *cur;208209for (; cur != range_end; ++cur) {210cur_val = *cur;211*cur = prev_val;212prev_val = cur_val;213}214// As long as this isn't the end we will always need to move at least one215// item (since we know that pos isn't the last item due to the check216// above).217if (range_end != end())218*cur = prev_val;219} else {220// FORWARD: Iterate from pos to end221const Iterator range_end = end();222Iterator cur = pos;223Iterator prev = cur;224++cur;225226for (; cur != range_end; prev = cur, ++cur)227*prev = *cur;228}229pop_back();230}231};232233template <typename T, size_t BLOCK_SIZE, bool REVERSE_ORDER>234LIBC_INLINE void BlockStore<T, BLOCK_SIZE, REVERSE_ORDER>::destroy(235BlockStore<T, BLOCK_SIZE, REVERSE_ORDER> *block_store) {236if (REVERSE_ORDER) {237auto current = block_store->current;238while (current->next != nullptr) {239auto temp = current;240current = current->next;241delete temp;242}243} else {244auto current = block_store->first.next;245while (current != nullptr) {246auto temp = current;247current = current->next;248delete temp;249}250}251block_store->current = nullptr;252block_store->fill_count = 0;253}254255// A convenience type for reverse order block stores.256template <typename T, size_t BLOCK_SIZE>257using ReverseOrderBlockStore = BlockStore<T, BLOCK_SIZE, true>;258259} // namespace LIBC_NAMESPACE_DECL260261#endif // LLVM_LIBC_SRC___SUPPORT_BLOCKSTORE_H262263264