1385961d7SDaniel Thornburgh //===-- Interface for freestore ------------------------------------------===// 2385961d7SDaniel Thornburgh // 3385961d7SDaniel Thornburgh // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4385961d7SDaniel Thornburgh // See https://llvm.org/LICENSE.txt for license information. 5385961d7SDaniel Thornburgh // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6385961d7SDaniel Thornburgh // 7385961d7SDaniel Thornburgh //===----------------------------------------------------------------------===// 8385961d7SDaniel Thornburgh 9385961d7SDaniel Thornburgh #ifndef LLVM_LIBC_SRC___SUPPORT_FREESTORE_H 10385961d7SDaniel Thornburgh #define LLVM_LIBC_SRC___SUPPORT_FREESTORE_H 11385961d7SDaniel Thornburgh 12385961d7SDaniel Thornburgh #include "freetrie.h" 13385961d7SDaniel Thornburgh 14385961d7SDaniel Thornburgh namespace LIBC_NAMESPACE_DECL { 15385961d7SDaniel Thornburgh 16385961d7SDaniel Thornburgh /// A best-fit store of variously-sized free blocks. Blocks can be inserted and 17385961d7SDaniel Thornburgh /// removed in logarithmic time. 18385961d7SDaniel Thornburgh class FreeStore { 19385961d7SDaniel Thornburgh public: 20385961d7SDaniel Thornburgh FreeStore() = default; 21385961d7SDaniel Thornburgh FreeStore(const FreeStore &other) = delete; 22385961d7SDaniel Thornburgh FreeStore &operator=(const FreeStore &other) = delete; 23385961d7SDaniel Thornburgh 24385961d7SDaniel Thornburgh /// Sets the range of possible block sizes. This can only be called when the 25385961d7SDaniel Thornburgh /// trie is empty. 26385961d7SDaniel Thornburgh LIBC_INLINE void set_range(FreeTrie::SizeRange range) { 27385961d7SDaniel Thornburgh large_trie.set_range(range); 28385961d7SDaniel Thornburgh } 29385961d7SDaniel Thornburgh 30385961d7SDaniel Thornburgh /// Insert a free block. If the block is too small to be tracked, nothing 31385961d7SDaniel Thornburgh /// happens. 32d121d71fSDaniel Thornburgh void insert(Block *block); 33385961d7SDaniel Thornburgh 34385961d7SDaniel Thornburgh /// Remove a free block. If the block is too small to be tracked, nothing 35385961d7SDaniel Thornburgh /// happens. 36d121d71fSDaniel Thornburgh void remove(Block *block); 37385961d7SDaniel Thornburgh 38385961d7SDaniel Thornburgh /// Remove a best-fit free block that can contain the given size when 39385961d7SDaniel Thornburgh /// allocated. Returns nullptr if there is no such block. 40d121d71fSDaniel Thornburgh Block *remove_best_fit(size_t size); 41385961d7SDaniel Thornburgh 42385961d7SDaniel Thornburgh private: 43385961d7SDaniel Thornburgh static constexpr size_t MIN_OUTER_SIZE = 44*cd92aedfSDaniel Thornburgh align_up(sizeof(Block) + sizeof(FreeList::Node), alignof(max_align_t)); 45385961d7SDaniel Thornburgh static constexpr size_t MIN_LARGE_OUTER_SIZE = 46*cd92aedfSDaniel Thornburgh align_up(sizeof(Block) + sizeof(FreeTrie::Node), alignof(max_align_t)); 47385961d7SDaniel Thornburgh static constexpr size_t NUM_SMALL_SIZES = 48*cd92aedfSDaniel Thornburgh (MIN_LARGE_OUTER_SIZE - MIN_OUTER_SIZE) / alignof(max_align_t); 49385961d7SDaniel Thornburgh 50d121d71fSDaniel Thornburgh LIBC_INLINE static bool too_small(Block *block) { 51385961d7SDaniel Thornburgh return block->outer_size() < MIN_OUTER_SIZE; 52385961d7SDaniel Thornburgh } 53d121d71fSDaniel Thornburgh LIBC_INLINE static bool is_small(Block *block) { 54385961d7SDaniel Thornburgh return block->outer_size() < MIN_LARGE_OUTER_SIZE; 55385961d7SDaniel Thornburgh } 56385961d7SDaniel Thornburgh 57d121d71fSDaniel Thornburgh FreeList &small_list(Block *block); 58385961d7SDaniel Thornburgh FreeList *find_best_small_fit(size_t size); 59385961d7SDaniel Thornburgh 60385961d7SDaniel Thornburgh cpp::array<FreeList, NUM_SMALL_SIZES> small_lists; 61385961d7SDaniel Thornburgh FreeTrie large_trie; 62385961d7SDaniel Thornburgh }; 63385961d7SDaniel Thornburgh 64d121d71fSDaniel Thornburgh LIBC_INLINE void FreeStore::insert(Block *block) { 65385961d7SDaniel Thornburgh if (too_small(block)) 66385961d7SDaniel Thornburgh return; 67385961d7SDaniel Thornburgh if (is_small(block)) 68385961d7SDaniel Thornburgh small_list(block).push(block); 69385961d7SDaniel Thornburgh else 70385961d7SDaniel Thornburgh large_trie.push(block); 71385961d7SDaniel Thornburgh } 72385961d7SDaniel Thornburgh 73d121d71fSDaniel Thornburgh LIBC_INLINE void FreeStore::remove(Block *block) { 74385961d7SDaniel Thornburgh if (too_small(block)) 75385961d7SDaniel Thornburgh return; 76385961d7SDaniel Thornburgh if (is_small(block)) { 77385961d7SDaniel Thornburgh small_list(block).remove( 78385961d7SDaniel Thornburgh reinterpret_cast<FreeList::Node *>(block->usable_space())); 79385961d7SDaniel Thornburgh } else { 80385961d7SDaniel Thornburgh large_trie.remove( 81385961d7SDaniel Thornburgh reinterpret_cast<FreeTrie::Node *>(block->usable_space())); 82385961d7SDaniel Thornburgh } 83385961d7SDaniel Thornburgh } 84385961d7SDaniel Thornburgh 85d121d71fSDaniel Thornburgh LIBC_INLINE Block *FreeStore::remove_best_fit(size_t size) { 86385961d7SDaniel Thornburgh if (FreeList *list = find_best_small_fit(size)) { 87d121d71fSDaniel Thornburgh Block *block = list->front(); 88385961d7SDaniel Thornburgh list->pop(); 89385961d7SDaniel Thornburgh return block; 90385961d7SDaniel Thornburgh } 91385961d7SDaniel Thornburgh if (FreeTrie::Node *best_fit = large_trie.find_best_fit(size)) { 92d121d71fSDaniel Thornburgh Block *block = best_fit->block(); 93385961d7SDaniel Thornburgh large_trie.remove(best_fit); 94385961d7SDaniel Thornburgh return block; 95385961d7SDaniel Thornburgh } 96385961d7SDaniel Thornburgh return nullptr; 97385961d7SDaniel Thornburgh } 98385961d7SDaniel Thornburgh 99d121d71fSDaniel Thornburgh LIBC_INLINE FreeList &FreeStore::small_list(Block *block) { 100385961d7SDaniel Thornburgh LIBC_ASSERT(is_small(block) && "only legal for small blocks"); 101*cd92aedfSDaniel Thornburgh return small_lists[(block->outer_size() - MIN_OUTER_SIZE) / 102*cd92aedfSDaniel Thornburgh alignof(max_align_t)]; 103385961d7SDaniel Thornburgh } 104385961d7SDaniel Thornburgh 105385961d7SDaniel Thornburgh LIBC_INLINE FreeList *FreeStore::find_best_small_fit(size_t size) { 106385961d7SDaniel Thornburgh for (FreeList &list : small_lists) 107385961d7SDaniel Thornburgh if (!list.empty() && list.size() >= size) 108385961d7SDaniel Thornburgh return &list; 109385961d7SDaniel Thornburgh return nullptr; 110385961d7SDaniel Thornburgh } 111385961d7SDaniel Thornburgh 112385961d7SDaniel Thornburgh } // namespace LIBC_NAMESPACE_DECL 113385961d7SDaniel Thornburgh 114385961d7SDaniel Thornburgh #endif // LLVM_LIBC_SRC___SUPPORT_FREESTORE_H 115