| //===-- Interface for freelist --------------------------------------------===// |
| // |
| // 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_FREELIST_H |
| #define LLVM_LIBC_SRC___SUPPORT_FREELIST_H |
| |
| #include "block.h" |
| |
| namespace LIBC_NAMESPACE_DECL { |
| |
| /// A circularly-linked FIFO list storing free Blocks. All Blocks on a list |
| /// are the same size. The blocks are referenced by Nodes in the list; the list |
| /// refers to these, but it does not own them. |
| /// |
| /// Allocating free blocks in FIFO order maximizes the amount of time before a |
| /// free block is reused. This in turn maximizes the number of opportunities for |
| /// it to be coalesced with an adjacent block, which tends to reduce heap |
| /// fragmentation. |
| class FreeList { |
| public: |
| class Node { |
| public: |
| /// @returns The block containing this node. |
| LIBC_INLINE const Block *block() const { |
| return Block::from_usable_space(this); |
| } |
| |
| /// @returns The block containing this node. |
| LIBC_INLINE Block *block() { return Block::from_usable_space(this); } |
| |
| /// @returns The inner size of blocks in the list containing this node. |
| LIBC_INLINE size_t size() const { return block()->inner_size(); } |
| |
| private: |
| // Circularly linked pointers to adjacent nodes. |
| Node *prev; |
| Node *next; |
| friend class FreeList; |
| }; |
| |
| LIBC_INLINE constexpr FreeList() : FreeList(nullptr) {} |
| LIBC_INLINE constexpr FreeList(Node *begin) : begin_(begin) {} |
| |
| LIBC_INLINE bool empty() const { return !begin_; } |
| |
| /// @returns The inner size of blocks in the list. |
| LIBC_INLINE size_t size() const { |
| LIBC_ASSERT(begin_ && "empty lists have no size"); |
| return begin_->size(); |
| } |
| |
| /// @returns The first node in the list. |
| LIBC_INLINE Node *begin() { return begin_; } |
| |
| /// @returns The first block in the list. |
| LIBC_INLINE Block *front() { return begin_->block(); } |
| |
| /// Push a block to the back of the list. |
| /// The block must be large enough to contain a node. |
| LIBC_INLINE void push(Block *block) { |
| LIBC_ASSERT(!block->used() && |
| "only free blocks can be placed on free lists"); |
| LIBC_ASSERT(block->inner_size_free() >= sizeof(FreeList) && |
| "block too small to accomodate free list node"); |
| push(new (block->usable_space()) Node); |
| } |
| |
| /// Push an already-constructed node to the back of the list. |
| /// This allows pushing derived node types with additional data. |
| void push(Node *node); |
| |
| /// Pop the first node from the list. |
| LIBC_INLINE void pop() { remove(begin_); } |
| |
| /// Remove an arbitrary node from the list. |
| void remove(Node *node); |
| |
| private: |
| Node *begin_; |
| }; |
| |
| } // namespace LIBC_NAMESPACE_DECL |
| |
| #endif // LLVM_LIBC_SRC___SUPPORT_FREELIST_H |