1 //===-- Interface for freelist_malloc -------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_LIBC_SRC___SUPPORT_FREELIST_H
10 #define LLVM_LIBC_SRC___SUPPORT_FREELIST_H
12 #include "src/__support/CPP/array.h"
13 #include "src/__support/CPP/cstddef.h"
14 #include "src/__support/CPP/new.h"
15 #include "src/__support/CPP/span.h"
16 #include "src/__support/fixedvector.h"
17 #include "src/__support/macros/config.h"
19 namespace LIBC_NAMESPACE_DECL
{
23 /// Basic [freelist](https://en.wikipedia.org/wiki/Free_list) implementation
24 /// for an allocator. This implementation buckets by chunk size, with a list
25 /// of user-provided buckets. Each bucket is a linked list of storage chunks.
26 /// Because this freelist uses the added chunks themselves as list nodes, there
27 /// is a lower bound of `sizeof(FreeList.FreeListNode)` bytes for chunks which
28 /// can be added to this freelist. There is also an implicit bucket for
29 /// "everything else", for chunks which do not fit into a bucket.
31 /// Each added chunk will be added to the smallest bucket under which it fits.
32 /// If it does not fit into any user-provided bucket, it will be added to the
35 /// As an example, assume that the `FreeList` is configured with buckets of
36 /// sizes {64, 128, 256, and 512} bytes. The internal state may look like the
40 /// bucket[0] (64B) --> chunk[12B] --> chunk[42B] --> chunk[64B] --> NULL
41 /// bucket[1] (128B) --> chunk[65B] --> chunk[72B] --> NULL
42 /// bucket[2] (256B) --> NULL
43 /// bucket[3] (512B) --> chunk[312B] --> chunk[512B] --> chunk[416B] --> NULL
44 /// bucket[4] (implicit) --> chunk[1024B] --> chunk[513B] --> NULL
47 /// Note that added chunks should be aligned to a 4-byte boundary.
48 template <size_t NUM_BUCKETS
= 6> class FreeList
{
50 // Remove copy/move ctors
51 FreeList(const FreeList
&other
) = delete;
52 FreeList(FreeList
&&other
) = delete;
53 FreeList
&operator=(const FreeList
&other
) = delete;
54 FreeList
&operator=(FreeList
&&other
) = delete;
56 /// Adds a chunk to this freelist.
57 bool add_chunk(cpp::span
<cpp::byte
> chunk
);
59 /// Finds an eligible chunk for an allocation of size `size`.
61 /// @note This returns the first allocation possible within a given bucket;
62 /// It does not currently optimize for finding the smallest chunk.
65 /// * On success - A span representing the chunk.
66 /// * On failure (e.g. there were no chunks available for that allocation) -
67 /// A span with a size of 0.
68 cpp::span
<cpp::byte
> find_chunk(size_t size
) const;
70 template <typename Cond
> cpp::span
<cpp::byte
> find_chunk_if(Cond op
) const;
72 /// Removes a chunk from this freelist.
73 bool remove_chunk(cpp::span
<cpp::byte
> chunk
);
75 /// For a given size, find which index into chunks_ the node should be written
77 constexpr size_t find_chunk_ptr_for_size(size_t size
, bool non_null
) const;
84 constexpr void set_freelist_node(FreeListNode
&node
,
85 cpp::span
<cpp::byte
> chunk
);
87 constexpr explicit FreeList(const cpp::array
<size_t, NUM_BUCKETS
> &sizes
)
88 : chunks_(NUM_BUCKETS
+ 1, 0), sizes_(sizes
.begin(), sizes
.end()) {}
91 FixedVector
<FreeList::FreeListNode
*, NUM_BUCKETS
+ 1> chunks_
;
92 FixedVector
<size_t, NUM_BUCKETS
> sizes_
;
95 template <size_t NUM_BUCKETS
>
96 constexpr void FreeList
<NUM_BUCKETS
>::set_freelist_node(FreeListNode
&node
,
97 span
<cpp::byte
> chunk
) {
98 // Add it to the correct list.
99 size_t chunk_ptr
= find_chunk_ptr_for_size(chunk
.size(), false);
100 node
.size
= chunk
.size();
101 node
.next
= chunks_
[chunk_ptr
];
102 chunks_
[chunk_ptr
] = &node
;
105 template <size_t NUM_BUCKETS
>
106 bool FreeList
<NUM_BUCKETS
>::add_chunk(span
<cpp::byte
> chunk
) {
107 // Check that the size is enough to actually store what we need
108 if (chunk
.size() < sizeof(FreeListNode
))
111 FreeListNode
*node
= ::new (chunk
.data()) FreeListNode
;
112 set_freelist_node(*node
, chunk
);
117 template <size_t NUM_BUCKETS
>
118 template <typename Cond
>
119 span
<cpp::byte
> FreeList
<NUM_BUCKETS
>::find_chunk_if(Cond op
) const {
120 for (FreeListNode
*node
: chunks_
) {
121 while (node
!= nullptr) {
122 span
<cpp::byte
> chunk(reinterpret_cast<cpp::byte
*>(node
), node
->size
);
133 template <size_t NUM_BUCKETS
>
134 span
<cpp::byte
> FreeList
<NUM_BUCKETS
>::find_chunk(size_t size
) const {
136 return span
<cpp::byte
>();
138 size_t chunk_ptr
= find_chunk_ptr_for_size(size
, true);
140 // Check that there's data. This catches the case where we run off the
142 if (chunks_
[chunk_ptr
] == nullptr)
143 return span
<cpp::byte
>();
145 // Now iterate up the buckets, walking each list to find a good candidate
146 for (size_t i
= chunk_ptr
; i
< chunks_
.size(); i
++) {
147 FreeListNode
*node
= chunks_
[static_cast<unsigned short>(i
)];
149 while (node
!= nullptr) {
150 if (node
->size
>= size
)
151 return span
<cpp::byte
>(reinterpret_cast<cpp::byte
*>(node
), node
->size
);
157 // If we get here, we've checked every block in every bucket. There's
158 // nothing that can support this allocation.
159 return span
<cpp::byte
>();
162 template <size_t NUM_BUCKETS
>
163 bool FreeList
<NUM_BUCKETS
>::remove_chunk(span
<cpp::byte
> chunk
) {
164 size_t chunk_ptr
= find_chunk_ptr_for_size(chunk
.size(), true);
167 if (chunks_
[chunk_ptr
] == nullptr)
170 FreeListNode
*node
= chunks_
[chunk_ptr
];
171 if (reinterpret_cast<cpp::byte
*>(node
) == chunk
.data()) {
172 chunks_
[chunk_ptr
] = node
->next
;
176 // No? Walk the nodes.
177 node
= chunks_
[chunk_ptr
];
179 while (node
->next
!= nullptr) {
180 if (reinterpret_cast<cpp::byte
*>(node
->next
) == chunk
.data()) {
181 // Found it, remove this node out of the chain
182 node
->next
= node
->next
->next
;
192 template <size_t NUM_BUCKETS
>
194 FreeList
<NUM_BUCKETS
>::find_chunk_ptr_for_size(size_t size
,
195 bool non_null
) const {
196 size_t chunk_ptr
= 0;
197 for (chunk_ptr
= 0u; chunk_ptr
< sizes_
.size(); chunk_ptr
++) {
198 if (sizes_
[chunk_ptr
] >= size
&&
199 (!non_null
|| chunks_
[chunk_ptr
] != nullptr)) {
207 } // namespace LIBC_NAMESPACE_DECL
209 #endif // LLVM_LIBC_SRC___SUPPORT_FREELIST_H