1 | //===-- Interface for freelist --------------------------------------------===// |
2 | // |
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 |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #ifndef LLVM_LIBC_SRC___SUPPORT_FREELIST_H |
10 | #define LLVM_LIBC_SRC___SUPPORT_FREELIST_H |
11 | |
12 | #include "block.h" |
13 | |
14 | namespace LIBC_NAMESPACE_DECL { |
15 | |
16 | /// A circularly-linked FIFO list storing free Blocks. All Blocks on a list |
17 | /// are the same size. The blocks are referenced by Nodes in the list; the list |
18 | /// refers to these, but it does not own them. |
19 | /// |
20 | /// Allocating free blocks in FIFO order maximizes the amount of time before a |
21 | /// free block is reused. This in turn maximizes the number of opportunities for |
22 | /// it to be coalesced with an adjacent block, which tends to reduce heap |
23 | /// fragmentation. |
24 | class FreeList { |
25 | public: |
26 | class Node { |
27 | public: |
28 | /// @returns The block containing this node. |
29 | LIBC_INLINE const Block *block() const { |
30 | return Block::from_usable_space(this); |
31 | } |
32 | |
33 | /// @returns The block containing this node. |
34 | LIBC_INLINE Block *block() { return Block::from_usable_space(this); } |
35 | |
36 | /// @returns The inner size of blocks in the list containing this node. |
37 | LIBC_INLINE size_t size() const { return block()->inner_size(); } |
38 | |
39 | private: |
40 | // Circularly linked pointers to adjacent nodes. |
41 | Node *prev; |
42 | Node *next; |
43 | friend class FreeList; |
44 | }; |
45 | |
46 | LIBC_INLINE constexpr FreeList() : FreeList(nullptr) {} |
47 | LIBC_INLINE constexpr FreeList(Node *begin) : begin_(begin) {} |
48 | |
49 | LIBC_INLINE bool empty() const { return !begin_; } |
50 | |
51 | /// @returns The inner size of blocks in the list. |
52 | LIBC_INLINE size_t size() const { |
53 | LIBC_ASSERT(begin_ && "empty lists have no size" ); |
54 | return begin_->size(); |
55 | } |
56 | |
57 | /// @returns The first node in the list. |
58 | LIBC_INLINE Node *begin() { return begin_; } |
59 | |
60 | /// @returns The first block in the list. |
61 | LIBC_INLINE Block *front() { return begin_->block(); } |
62 | |
63 | /// Push a block to the back of the list. |
64 | /// The block must be large enough to contain a node. |
65 | LIBC_INLINE void push(Block *block) { |
66 | LIBC_ASSERT(!block->used() && |
67 | "only free blocks can be placed on free lists" ); |
68 | LIBC_ASSERT(block->inner_size_free() >= sizeof(FreeList) && |
69 | "block too small to accomodate free list node" ); |
70 | push(new (block->usable_space()) Node); |
71 | } |
72 | |
73 | /// Push an already-constructed node to the back of the list. |
74 | /// This allows pushing derived node types with additional data. |
75 | void push(Node *node); |
76 | |
77 | /// Pop the first node from the list. |
78 | LIBC_INLINE void pop() { remove(node: begin_); } |
79 | |
80 | /// Remove an arbitrary node from the list. |
81 | void remove(Node *node); |
82 | |
83 | private: |
84 | Node *begin_; |
85 | }; |
86 | |
87 | } // namespace LIBC_NAMESPACE_DECL |
88 | |
89 | #endif // LLVM_LIBC_SRC___SUPPORT_FREELIST_H |
90 | |