/** * @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 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 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(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(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 _array; #else utl::vector _array; #endif u32 _next_free_index{ u32_invalid_id }; u32 _size{ 0 }; }; }