xref: /llvm-project/libc/src/__support/freestore.h (revision cd92aedf1bb67f643fb9656ab8d28fc5eab05083)
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