//===-- Interface for freestore ------------------------------------------===// // // 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_FREESTORE_H #define LLVM_LIBC_SRC___SUPPORT_FREESTORE_H #include "freetrie.h" namespace LIBC_NAMESPACE_DECL { /// A best-fit store of variously-sized free blocks. Blocks can be inserted and /// removed in logarithmic time. class FreeStore { public: FreeStore() = default; FreeStore(const FreeStore &other) = delete; FreeStore &operator=(const FreeStore &other) = delete; /// Sets the range of possible block sizes. This can only be called when the /// trie is empty. LIBC_INLINE void set_range(FreeTrie::SizeRange range) { large_trie.set_range(range); } /// Insert a free block. If the block is too small to be tracked, nothing /// happens. void insert(Block *block); /// Remove a free block. If the block is too small to be tracked, nothing /// happens. void remove(Block *block); /// Remove a best-fit free block that can contain the given size when /// allocated. Returns nullptr if there is no such block. Block *remove_best_fit(size_t size); private: static constexpr size_t ALIGNMENT = alignof(max_align_t); static constexpr size_t MIN_OUTER_SIZE = align_up(Block::BLOCK_OVERHEAD + sizeof(FreeList::Node), ALIGNMENT); static constexpr size_t MIN_LARGE_OUTER_SIZE = align_up(Block::BLOCK_OVERHEAD + sizeof(FreeTrie::Node), ALIGNMENT); static constexpr size_t NUM_SMALL_SIZES = (MIN_LARGE_OUTER_SIZE - MIN_OUTER_SIZE) / ALIGNMENT; LIBC_INLINE static bool too_small(Block *block) { return block->outer_size() < MIN_OUTER_SIZE; } LIBC_INLINE static bool is_small(Block *block) { return block->outer_size() < MIN_LARGE_OUTER_SIZE; } FreeList &small_list(Block *block); FreeList *find_best_small_fit(size_t size); cpp::array small_lists; FreeTrie large_trie; }; LIBC_INLINE void FreeStore::insert(Block *block) { if (too_small(block)) return; if (is_small(block)) small_list(block).push(block); else large_trie.push(block); } LIBC_INLINE void FreeStore::remove(Block *block) { if (too_small(block)) return; if (is_small(block)) { small_list(block).remove( reinterpret_cast(block->usable_space())); } else { large_trie.remove( reinterpret_cast(block->usable_space())); } } LIBC_INLINE Block *FreeStore::remove_best_fit(size_t size) { if (FreeList *list = find_best_small_fit(size)) { Block *block = list->front(); list->pop(); return block; } if (FreeTrie::Node *best_fit = large_trie.find_best_fit(size)) { Block *block = best_fit->block(); large_trie.remove(best_fit); return block; } return nullptr; } LIBC_INLINE FreeList &FreeStore::small_list(Block *block) { LIBC_ASSERT(is_small(block) && "only legal for small blocks"); return small_lists[(block->outer_size() - MIN_OUTER_SIZE) / ALIGNMENT]; } LIBC_INLINE FreeList *FreeStore::find_best_small_fit(size_t size) { for (FreeList &list : small_lists) if (!list.empty() && list.size() >= size) return &list; return nullptr; } } // namespace LIBC_NAMESPACE_DECL #endif // LLVM_LIBC_SRC___SUPPORT_FREESTORE_H