170 lines
3.4 KiB
C++
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 };
|
|
};
|
|
|
|
}
|