#ifndef LLVM_LIBC_SRC___SUPPORT_FREELIST_H
#define LLVM_LIBC_SRC___SUPPORT_FREELIST_H
#include "src/__support/CPP/array.h"
#include "src/__support/CPP/cstddef.h"
#include "src/__support/CPP/new.h"
#include "src/__support/CPP/span.h"
#include "src/__support/fixedvector.h"
#include "src/__support/macros/config.h"
namespace LIBC_NAMESPACE_DECL {
using cpp::span;
template <size_t NUM_BUCKETS = 6> class FreeList {
public:
FreeList(const FreeList &other) = delete;
FreeList(FreeList &&other) = delete;
FreeList &operator=(const FreeList &other) = delete;
FreeList &operator=(FreeList &&other) = delete;
bool add_chunk(cpp::span<cpp::byte> chunk);
cpp::span<cpp::byte> find_chunk(size_t size) const;
template <typename Cond> cpp::span<cpp::byte> find_chunk_if(Cond op) const;
bool remove_chunk(cpp::span<cpp::byte> chunk);
constexpr size_t find_chunk_ptr_for_size(size_t size, bool non_null) const;
struct FreeListNode {
FreeListNode *next;
size_t size;
};
constexpr void set_freelist_node(FreeListNode &node,
cpp::span<cpp::byte> chunk);
constexpr explicit FreeList(const cpp::array<size_t, NUM_BUCKETS> &sizes)
: chunks_(NUM_BUCKETS + 1, 0), sizes_(sizes.begin(), sizes.end()) {}
private:
FixedVector<FreeList::FreeListNode *, NUM_BUCKETS + 1> chunks_;
FixedVector<size_t, NUM_BUCKETS> sizes_;
};
template <size_t NUM_BUCKETS>
constexpr void FreeList<NUM_BUCKETS>::set_freelist_node(FreeListNode &node,
span<cpp::byte> chunk) {
size_t chunk_ptr = find_chunk_ptr_for_size(chunk.size(), false);
node.size = chunk.size();
node.next = chunks_[chunk_ptr];
chunks_[chunk_ptr] = &node;
}
template <size_t NUM_BUCKETS>
bool FreeList<NUM_BUCKETS>::add_chunk(span<cpp::byte> chunk) {
if (chunk.size() < sizeof(FreeListNode))
return false;
FreeListNode *node = ::new (chunk.data()) FreeListNode;
set_freelist_node(*node, chunk);
return true;
}
template <size_t NUM_BUCKETS>
template <typename Cond>
span<cpp::byte> FreeList<NUM_BUCKETS>::find_chunk_if(Cond op) const {
for (FreeListNode *node : chunks_) {
while (node != nullptr) {
span<cpp::byte> chunk(reinterpret_cast<cpp::byte *>(node), node->size);
if (op(chunk))
return chunk;
node = node->next;
}
}
return {};
}
template <size_t NUM_BUCKETS>
span<cpp::byte> FreeList<NUM_BUCKETS>::find_chunk(size_t size) const {
if (size == 0)
return span<cpp::byte>();
size_t chunk_ptr = find_chunk_ptr_for_size(size, true);
if (chunks_[chunk_ptr] == nullptr)
return span<cpp::byte>();
for (size_t i = chunk_ptr; i < chunks_.size(); i++) {
FreeListNode *node = chunks_[static_cast<unsigned short>(i)];
while (node != nullptr) {
if (node->size >= size)
return span<cpp::byte>(reinterpret_cast<cpp::byte *>(node), node->size);
node = node->next;
}
}
return span<cpp::byte>();
}
template <size_t NUM_BUCKETS>
bool FreeList<NUM_BUCKETS>::remove_chunk(span<cpp::byte> chunk) {
size_t chunk_ptr = find_chunk_ptr_for_size(chunk.size(), true);
if (chunks_[chunk_ptr] == nullptr)
return false;
FreeListNode *node = chunks_[chunk_ptr];
if (reinterpret_cast<cpp::byte *>(node) == chunk.data()) {
chunks_[chunk_ptr] = node->next;
return true;
}
node = chunks_[chunk_ptr];
while (node->next != nullptr) {
if (reinterpret_cast<cpp::byte *>(node->next) == chunk.data()) {
node->next = node->next->next;
return true;
}
node = node->next;
}
return false;
}
template <size_t NUM_BUCKETS>
constexpr size_t
FreeList<NUM_BUCKETS>::find_chunk_ptr_for_size(size_t size,
bool non_null) const {
size_t chunk_ptr = 0;
for (chunk_ptr = 0u; chunk_ptr < sizes_.size(); chunk_ptr++) {
if (sizes_[chunk_ptr] >= size &&
(!non_null || chunks_[chunk_ptr] != nullptr)) {
break;
}
}
return chunk_ptr;
}
}
#endif