Files
DX12/Engine/Utilities/FreeList.h
2026-03-19 18:27:49 +08:00

170 lines
3.4 KiB
C++

/**
* @file FreeList.h
* @brief 带复用槽位的稀疏对象容器模板。
* @details
* free_list 维护“已使用槽位 + 空闲链”结构,支持:
* - O(1) 近似开销的新增与删除;
* - 删除后槽位复用,降低频繁分配释放成本;
* - 以索引作为外部句柄,与 id 系统协同使用。
*/
#pragma once
#include "CommonHeader.h"
namespace XEngine::utl {
#if USE_STL_VECTOR
#pragma message("WARNING: using utl::free_list with std::vector result in duplicate calls to class constructor!");
#endif
template<typename T>
class free_list
{
static_assert(sizeof(T) >= sizeof(u32));
public:
/**
* @brief 构造空容器。
*/
free_list() = default;
/**
* @brief 预留底层存储容量。
* @param count 预留元素数量。
*/
explicit free_list(u32 count)
{
_array.reserve(count);
}
/**
* @brief 销毁容器并校验无存活元素。
*/
~free_list()
{
assert(!_size);
#if USE_STL_VECTOR
memset(_array.data(), 0, _array.size() * sizeof(T));
#endif
}
/**
* @brief 新增一个元素并返回槽位 ID。
* @tparam params 构造参数类型。
* @param p 构造参数。
* @return 新元素 ID。
*/
template<class ... params>
constexpr u32 add(params&&... p)
{
u32 id{ u32_invalid_id };
if (_next_free_index == u32_invalid_id)
{
id = (u32)_array.size();
_array.emplace_back(std::forward<params>(p)...);
}
else
{
id = _next_free_index;
assert(id < _array.size() && already_removed(id));
_next_free_index = *(const u32 *const)std::addressof(_array[id]);
new (std::addressof(_array[id])) T(std::forward<params>(p)...);
}
++_size;
return id;
}
/**
* @brief 删除指定 ID 的元素并回收到空闲链。
* @param id 元素 ID。
*/
constexpr void remove(u32 id)
{
assert(id < _array.size() && !already_removed(id));
T& item{ _array[id] };
item.~T();
DEBUG_OP(memset(std::addressof(_array[id]), 0xcc, sizeof(T)));
*(u32 *const)std::addressof(_array[id]) = _next_free_index;
_next_free_index = id;
--_size;
}
/**
* @brief 获取当前有效元素数量。
* @return 元素数量。
*/
constexpr u32 size() const
{
return _size;
}
/**
* @brief 获取当前已分配槽位总数。
* @return 容量值。
*/
constexpr u32 capacity() const
{
return _array.size();
}
/**
* @brief 判断容器是否为空。
* @return 空返回 true。
*/
constexpr bool empty() const
{
return _size == 0;
}
/**
* @brief 按 ID 访问元素。
* @param id 元素 ID。
* @return 元素引用。
*/
[[nodiscard]] constexpr T& operator[](u32 id)
{
assert(id < _array.size() && !already_removed(id));
return _array[id];
}
/**
* @brief 按 ID 访问常量元素。
* @param id 元素 ID。
* @return 常量元素引用。
*/
[[nodiscard]] constexpr const T& operator[](u32 id) const
{
assert(id < _array.size() && !already_removed(id));
return _array[id];
}
private:
constexpr bool already_removed(u32 id) const
{
if constexpr (sizeof(T) > sizeof(u32))
{
u32 i{ sizeof(u32) };
const u8 *const p{ (const u8 *const)std::addressof(_array[id]) };
while ((p[i] == 0xcc) && (i < sizeof(T))) ++i;
return i == sizeof(T);
}
else
{
return true;
}
}
#if USE_STL_VECTOR
utl::vector<T> _array;
#else
utl::vector<T, false> _array;
#endif
u32 _next_free_index{ u32_invalid_id };
u32 _size{ 0 };
};
}