支持分层 BitMask 自动扩容,避免用户超过组件后报错问题
This commit is contained in:
@@ -1,15 +1,16 @@
|
||||
/**
|
||||
* 64位掩码兼容层
|
||||
*/
|
||||
|
||||
/**
|
||||
* 64位掩码数据结构,使用两个32位整数表示
|
||||
* 位掩码数据结构
|
||||
* 基础模式(64位):使用 lo + hi 存储 64 位,segments 为空
|
||||
* 扩展模式(128+位):lo + hi 作为第一段,segments 存储额外的 64 位段
|
||||
* segments[0] 对应 bit 64-127,segments[1] 对应 bit 128-191,以此类推
|
||||
*/
|
||||
export interface BitMask64Data {
|
||||
/** 低32位 */
|
||||
/** 低32位(bit 0-31) */
|
||||
lo: number;
|
||||
/** 高32位 */
|
||||
/** 高32位(bit 32-63) */
|
||||
hi: number;
|
||||
/** 扩展段数组,每个元素是一个 64 位段,用于超过 64 位的场景 */
|
||||
segments?: BitMask64Data[];
|
||||
}
|
||||
|
||||
export class BitMask64Utils {
|
||||
@@ -55,12 +56,44 @@ export class BitMask64Utils {
|
||||
|
||||
/**
|
||||
* 检查掩码是否包含所有指定的位
|
||||
* 支持扩展模式,自动处理超过 64 位的掩码
|
||||
* @param mask 要检查的掩码
|
||||
* @param bits 指定的位模式
|
||||
* @returns 如果掩码包含bits中的所有位则返回true
|
||||
*/
|
||||
public static hasAll(mask: BitMask64Data, bits: BitMask64Data): boolean {
|
||||
return (mask.lo & bits.lo) === bits.lo && (mask.hi & bits.hi) === bits.hi;
|
||||
// 检查第一个 64 位段
|
||||
if ((mask.lo & bits.lo) !== bits.lo || (mask.hi & bits.hi) !== bits.hi) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果 bits 没有扩展段,检查完成
|
||||
if (!bits.segments || bits.segments.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 如果 bits 有扩展段但 mask 没有,返回 false
|
||||
if (!mask.segments || mask.segments.length === 0) {
|
||||
// 检查 bits 的扩展段是否全为 0
|
||||
return bits.segments.every(seg => BitMask64Utils.isZero(seg));
|
||||
}
|
||||
|
||||
// 递归检查每个扩展段
|
||||
const minSegments = Math.min(mask.segments.length, bits.segments.length);
|
||||
for (let i = 0; i < minSegments; i++) {
|
||||
if (!BitMask64Utils.hasAll(mask.segments[i], bits.segments[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果 bits 有更多段,检查这些段是否为空
|
||||
for (let i = minSegments; i < bits.segments.length; i++) {
|
||||
if (!BitMask64Utils.isZero(bits.segments[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -234,7 +267,98 @@ export class BitMask64Utils {
|
||||
hi &= hi - 1;
|
||||
count++;
|
||||
}
|
||||
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置扩展位(支持超过 64 位的索引)
|
||||
* @param mask 要修改的掩码(原地修改)
|
||||
* @param bitIndex 位索引(可以超过 63)
|
||||
*/
|
||||
public static setBitExtended(mask: BitMask64Data, bitIndex: number): void {
|
||||
if (bitIndex < 0) {
|
||||
throw new Error('Bit index cannot be negative');
|
||||
}
|
||||
|
||||
if (bitIndex < 64) {
|
||||
BitMask64Utils.setBit(mask, bitIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算段索引和段内位索引
|
||||
const segmentIndex = Math.floor(bitIndex / 64) - 1;
|
||||
const localBitIndex = bitIndex % 64;
|
||||
|
||||
// 确保 segments 数组存在
|
||||
if (!mask.segments) {
|
||||
mask.segments = [];
|
||||
}
|
||||
|
||||
// 扩展 segments 数组
|
||||
while (mask.segments.length <= segmentIndex) {
|
||||
mask.segments.push(BitMask64Utils.clone(BitMask64Utils.ZERO));
|
||||
}
|
||||
|
||||
// 设置对应段的位
|
||||
BitMask64Utils.setBit(mask.segments[segmentIndex], localBitIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取扩展位(支持超过 64 位的索引)
|
||||
* @param mask 要检查的掩码
|
||||
* @param bitIndex 位索引(可以超过 63)
|
||||
* @returns 如果位被设置则返回 true
|
||||
*/
|
||||
public static getBitExtended(mask: BitMask64Data, bitIndex: number): boolean {
|
||||
if (bitIndex < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bitIndex < 64) {
|
||||
const testMask = BitMask64Utils.create(bitIndex);
|
||||
return BitMask64Utils.hasAny(mask, testMask);
|
||||
}
|
||||
|
||||
if (!mask.segments) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const segmentIndex = Math.floor(bitIndex / 64) - 1;
|
||||
if (segmentIndex >= mask.segments.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const localBitIndex = bitIndex % 64;
|
||||
const testMask = BitMask64Utils.create(localBitIndex);
|
||||
return BitMask64Utils.hasAny(mask.segments[segmentIndex], testMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除扩展位(支持超过 64 位的索引)
|
||||
* @param mask 要修改的掩码(原地修改)
|
||||
* @param bitIndex 位索引(可以超过 63)
|
||||
*/
|
||||
public static clearBitExtended(mask: BitMask64Data, bitIndex: number): void {
|
||||
if (bitIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bitIndex < 64) {
|
||||
BitMask64Utils.clearBit(mask, bitIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mask.segments) {
|
||||
return;
|
||||
}
|
||||
|
||||
const segmentIndex = Math.floor(bitIndex / 64) - 1;
|
||||
if (segmentIndex >= mask.segments.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const localBitIndex = bitIndex % 64;
|
||||
BitMask64Utils.clearBit(mask.segments[segmentIndex], localBitIndex);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
import { BitMask64Data, BitMask64Utils } from './BigIntCompatibility';
|
||||
|
||||
/**
|
||||
* 64位位集合类,用于高效的位操作
|
||||
* 支持最多64个位的设置、清除、查询和逻辑运算
|
||||
* 位集合类,用于高效的位操作
|
||||
* 自动扩展支持:默认 64 位,超过时自动扩展到 128/256 位
|
||||
* 扩展模式性能略有下降,但仍然比数组遍历快得多
|
||||
*/
|
||||
export class Bits {
|
||||
/** 存储位数据的64位掩码 */
|
||||
/** 存储位数据的掩码,支持扩展 */
|
||||
private _value: BitMask64Data;
|
||||
|
||||
/**
|
||||
@@ -27,36 +28,29 @@ export class Bits {
|
||||
|
||||
/**
|
||||
* 设置指定位为1
|
||||
* @param index 位索引,范围 [0, 63]
|
||||
* @throws 当位索引为负数或超过64位限制时抛出错误
|
||||
* 自动扩展:当索引超过 64 时,自动扩展到 128/256 位
|
||||
* @param index 位索引(0-based)
|
||||
* @throws 当位索引为负数时抛出错误
|
||||
*/
|
||||
public set(index: number): void {
|
||||
if (index < 0) {
|
||||
throw new Error('Bit index cannot be negative');
|
||||
}
|
||||
|
||||
if (index >= 64) {
|
||||
throw new Error('Bit index exceeds 64-bit limit. ECS framework supports max 64 component types.');
|
||||
}
|
||||
|
||||
BitMask64Utils.setBit(this._value, index);
|
||||
|
||||
BitMask64Utils.setBitExtended(this._value, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除指定位为0
|
||||
* @param index 位索引,范围 [0, 63]
|
||||
* @param index 位索引
|
||||
* @throws 当位索引为负数时抛出错误
|
||||
*/
|
||||
public clear(index: number): void {
|
||||
if (index < 0) {
|
||||
throw new Error('Bit index cannot be negative');
|
||||
}
|
||||
|
||||
if (index >= 64) {
|
||||
return;
|
||||
}
|
||||
|
||||
BitMask64Utils.clearBit(this._value, index);
|
||||
|
||||
BitMask64Utils.clearBitExtended(this._value, index);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,12 +59,7 @@ export class Bits {
|
||||
* @returns 如果位被设置为1则返回true,否则返回false
|
||||
*/
|
||||
public get(index: number): boolean {
|
||||
if (index < 0 || index >= 64) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const mask = BitMask64Utils.create(index);
|
||||
return BitMask64Utils.hasAny(this._value, mask);
|
||||
return BitMask64Utils.getBitExtended(this._value, index);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,6 +5,7 @@ import { ComponentType } from "../../Types";
|
||||
/**
|
||||
* 组件类型管理器
|
||||
* 负责管理组件类型的注册和ID分配
|
||||
* 支持无限数量的组件类型(通过自动扩展 BitMask)
|
||||
*/
|
||||
export class ComponentTypeManager {
|
||||
private static _instance: ComponentTypeManager;
|
||||
@@ -31,13 +32,13 @@ export class ComponentTypeManager {
|
||||
*/
|
||||
public getTypeId(componentType: ComponentType): number {
|
||||
let typeId = this._componentTypes.get(componentType);
|
||||
|
||||
|
||||
if (typeId === undefined) {
|
||||
typeId = this._nextTypeId++;
|
||||
this._componentTypes.set(componentType, typeId);
|
||||
this._typeNames.set(typeId, getComponentTypeName(componentType));
|
||||
}
|
||||
|
||||
|
||||
return typeId;
|
||||
}
|
||||
|
||||
|
||||
@@ -438,9 +438,15 @@ describe('Bits - 高性能位操作类测试', () => {
|
||||
expect(bits.cardinality()).toBe(1);
|
||||
});
|
||||
|
||||
it('超过64位索引应该抛出错误', () => {
|
||||
expect(() => bits.set(64)).toThrow('Bit index exceeds 64-bit limit');
|
||||
expect(() => bits.set(100)).toThrow('Bit index exceeds 64-bit limit');
|
||||
it('超过64位索引应该自动扩展', () => {
|
||||
// 现在支持自动扩展到 128/256 位
|
||||
expect(() => bits.set(64)).not.toThrow();
|
||||
expect(() => bits.set(100)).not.toThrow();
|
||||
|
||||
// 验证扩展后的位可以正确读取
|
||||
expect(bits.get(64)).toBe(true);
|
||||
expect(bits.get(100)).toBe(true);
|
||||
expect(bits.get(65)).toBe(false);
|
||||
});
|
||||
|
||||
it('64位范围内的位运算应该正确', () => {
|
||||
|
||||
Reference in New Issue
Block a user