blob: 97197dda4b546b98f3e5e8f2fa0e02c7171e96eb [file] [log] [blame]
//===-- Interface for freestore ------------------------------------------===//
//
// 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_FREESTORE_H
#define LLVM_LIBC_SRC___SUPPORT_FREESTORE_H
#include "freetrie.h"
namespace LIBC_NAMESPACE_DECL {
/// A best-fit store of variously-sized free blocks. Blocks can be inserted and
/// removed in logarithmic time.
class FreeStore {
public:
FreeStore() = default;
FreeStore(const FreeStore &other) = delete;
FreeStore &operator=(const FreeStore &other) = delete;
/// Sets the range of possible block sizes. This can only be called when the
/// trie is empty.
LIBC_INLINE void set_range(FreeTrie::SizeRange range) {
large_trie.set_range(range);
}
/// Insert a free block. If the block is too small to be tracked, nothing
/// happens.
void insert(Block *block);
/// Remove a free block. If the block is too small to be tracked, nothing
/// happens.
void remove(Block *block);
/// Remove a best-fit free block that can contain the given size when
/// allocated. Returns nullptr if there is no such block.
Block *remove_best_fit(size_t size);
private:
static constexpr size_t ALIGNMENT = alignof(max_align_t);
static constexpr size_t MIN_OUTER_SIZE =
align_up(Block::BLOCK_OVERHEAD + sizeof(FreeList::Node), ALIGNMENT);
static constexpr size_t MIN_LARGE_OUTER_SIZE =
align_up(Block::BLOCK_OVERHEAD + sizeof(FreeTrie::Node), ALIGNMENT);
static constexpr size_t NUM_SMALL_SIZES =
(MIN_LARGE_OUTER_SIZE - MIN_OUTER_SIZE) / ALIGNMENT;
LIBC_INLINE static bool too_small(Block *block) {
return block->outer_size() < MIN_OUTER_SIZE;
}
LIBC_INLINE static bool is_small(Block *block) {
return block->outer_size() < MIN_LARGE_OUTER_SIZE;
}
FreeList &small_list(Block *block);
FreeList *find_best_small_fit(size_t size);
cpp::array<FreeList, NUM_SMALL_SIZES> small_lists;
FreeTrie large_trie;
};
LIBC_INLINE void FreeStore::insert(Block *block) {
if (too_small(block))
return;
if (is_small(block))
small_list(block).push(block);
else
large_trie.push(block);
}
LIBC_INLINE void FreeStore::remove(Block *block) {
if (too_small(block))
return;
if (is_small(block)) {
small_list(block).remove(
reinterpret_cast<FreeList::Node *>(block->usable_space()));
} else {
large_trie.remove(
reinterpret_cast<FreeTrie::Node *>(block->usable_space()));
}
}
LIBC_INLINE Block *FreeStore::remove_best_fit(size_t size) {
if (FreeList *list = find_best_small_fit(size)) {
Block *block = list->front();
list->pop();
return block;
}
if (FreeTrie::Node *best_fit = large_trie.find_best_fit(size)) {
Block *block = best_fit->block();
large_trie.remove(best_fit);
return block;
}
return nullptr;
}
LIBC_INLINE FreeList &FreeStore::small_list(Block *block) {
LIBC_ASSERT(is_small(block) && "only legal for small blocks");
return small_lists[(block->outer_size() - MIN_OUTER_SIZE) / ALIGNMENT];
}
LIBC_INLINE FreeList *FreeStore::find_best_small_fit(size_t size) {
for (FreeList &list : small_lists)
if (!list.empty() && list.size() >= size)
return &list;
return nullptr;
}
} // namespace LIBC_NAMESPACE_DECL
#endif // LLVM_LIBC_SRC___SUPPORT_FREESTORE_H