/** * @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, 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 }