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

144 lines
3.3 KiB
C++

/**
* @file Id.h
* @brief 使用“索引 + 代数”打包方案的 32 位标识符工具。
* @details
* 位布局:
* - 低位:索引
* - 高位:代数
*
* 当索引被复用时,通过递增代数降低悬挂句柄误用风险。
*/
#pragma once
#include "CommonHeader.h"
namespace XEngine::id
{
/**
* @brief 标识符相关类型与常量。
*/
using id_type = u32;
namespace detail {
/**
* @brief 高位代数字段的位宽。
*/
constexpr u32 generation_bits{ 8 };
/**
* @brief 低位索引字段的位宽。
*/
constexpr u32 index_bit{ sizeof(id_type) * 8 - generation_bits };
/**
* @brief 用于提取索引字段的掩码。
*/
constexpr id_type index_mask{ (id_type{1} << index_bit) - 1 };
/**
* @brief 右移后用于提取代数字段的掩码。
*/
constexpr id_type generation_mask{ (id_type{1} << generation_bits) - 1 };
}
/**
* @brief 表示无效标识符的哨兵值。
*/
constexpr id_type invalid_id{ (id_type)-1 };
/**
* @brief 延迟回收删除项时的触发阈值。
*/
constexpr u32 min_deleted_elements{ 1024 };
/**
* @brief 可容纳代数字段的最小无符号整型。
*/
using generation_type = std::conditional_t<detail::generation_bits <= 16, std::conditional_t<detail::generation_bits <= 8, u8, u16>, u32>;
static_assert(sizeof(generation_type) * 8 >= detail::generation_bits);
static_assert((sizeof(id_type) - sizeof(generation_type)) > 0);
/**
* @brief 判断标识符是否有效。
* @param id 打包后的标识符。
* @return 当 @p id 不是无效哨兵值时返回 true。
*/
constexpr bool
is_valid(id_type id)
{
return id != invalid_id;
}
/**
* @brief 从打包标识符中提取索引字段。
* @param id 打包后的标识符。
* @return 低位索引字段。
* @pre 提取后的索引不能等于保留的全 1 索引值。
*/
constexpr id_type
index(id_type id)
{
id_type index{ id & detail::index_mask };
assert(index != detail::index_mask);
return index;
}
/**
* @brief 从打包标识符中提取代数字段。
* @param id 打包后的标识符。
* @return 高位代数字段。
*/
constexpr id_type
generation(id_type id)
{
return (id >> detail::index_bit) & detail::generation_mask;
}
/**
* @brief 为同一索引生成下一代标识符。
* @param id 当前打包标识符。
* @return 索引不变且代数递增后的标识符。
* @pre 代数递增后不能溢出到保留范围。
*/
constexpr id_type
new_generation(id_type id)
{
const id_type generation{ id::generation(id) + 1 };
assert(generation < (((u64)1 << detail::generation_bits) - 1));
return index(id) | (generation << detail::index_bit);
}
#if _DEBUG
namespace detail {
struct id_base
{
constexpr explicit id_base(id_type id) : _id(id) {}
constexpr operator id_type() const { return _id; }
private:
id_type _id;
};
}
/**
* @brief 在调试构建下声明强类型标识符。
* @details
* 调试构建使用派生自 id_base 的包装类型。
* 发布构建退化为 id_type 别名以保持零开销抽象。
*/
#define DEFINE_TYPED_ID(name) \
struct name final : id::detail::id_base \
{ \
constexpr explicit name(id::id_type id) \
: id_base{id}{} \
constexpr name() : id_base{ 0 }{} \
};
#else
/**
* @brief 发布模式下的零运行时开销类型别名。
*/
#define DEFINE_TYPED_ID(name) using name = id::id_type;
#endif
}