From da8b7cf60154266f3fdbc042682b01ad54e8cb16 Mon Sep 17 00:00:00 2001 From: MirageTank <1455496974@qq.com> Date: Fri, 3 Oct 2025 16:55:07 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E4=BD=8D=E6=8E=A9=E7=A0=81?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E7=BB=93=E6=9E=84=EF=BC=8C=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E9=83=A8=E5=88=86=E6=96=B9=E6=B3=95=E6=9C=AA=E8=80=83=E8=99=91?= =?UTF-8?q?=E6=89=A9=E5=B1=95=E4=BD=8D=E7=9A=84=E9=97=AE=E9=A2=98=20-=20?= =?UTF-8?q?=E6=89=80=E6=9C=89=E6=93=8D=E4=BD=9C=E5=9D=87=E8=80=83=E8=99=91?= =?UTF-8?q?=E6=89=A9=E5=B1=95=E4=BD=8D=E3=80=81=E6=89=A9=E5=B1=95=E9=95=BF?= =?UTF-8?q?=E5=BA=A6=E4=B8=8D=E4=B8=80=E8=87=B4=E7=9A=84=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E5=9C=BA=E6=99=AF=EF=BC=8C=E6=97=A0=E6=84=9F=E6=89=A9=E5=AE=B9?= =?UTF-8?q?=E6=8E=A9=E7=A0=81=E4=BD=8D=20-=20=E4=BD=BF=E7=94=A8=E5=AE=9A?= =?UTF-8?q?=E9=95=BF=E6=95=B0=E7=BB=84=E5=AD=98=E5=82=A8=E9=AB=98=E4=BD=8E?= =?UTF-8?q?=E4=BD=8D=EF=BC=8C=E9=81=8D=E5=8E=86=E5=8F=8B=E5=A5=BD=EF=BC=8C?= =?UTF-8?q?=E4=B8=BA=E9=AB=98=E6=95=88=E5=93=88=E5=B8=8C=E8=AE=A1=E7=AE=97?= =?UTF-8?q?=E6=8F=90=E4=BE=9B=E7=BB=93=E6=9E=84=E6=94=AF=E6=8C=81=20-=20?= =?UTF-8?q?=E8=A1=A5=E5=85=85=E7=9B=B8=E5=BA=94=E5=8D=95=E5=85=83=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=EF=BC=8C=E8=A6=86=E7=9B=96=E6=89=80=E6=9C=89=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E5=8F=8A=E5=88=86=E6=94=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ComponentStorage/ComponentRegistry.ts | 5 +- packages/core/src/ECS/Core/QuerySystem.ts | 31 +- packages/core/src/ECS/Entity.ts | 4 +- .../core/src/ECS/Utils/BigIntCompatibility.ts | 529 ++++++++++-------- packages/core/src/ECS/Utils/Bits.ts | 50 +- .../ECS/Utils/BigIntCompatibility.test.ts | 392 ++++++------- 6 files changed, 482 insertions(+), 529 deletions(-) diff --git a/packages/core/src/ECS/Core/ComponentStorage/ComponentRegistry.ts b/packages/core/src/ECS/Core/ComponentStorage/ComponentRegistry.ts index 6c3500bf..6bd38eb7 100644 --- a/packages/core/src/ECS/Core/ComponentStorage/ComponentRegistry.ts +++ b/packages/core/src/ECS/Core/ComponentStorage/ComponentRegistry.ts @@ -54,10 +54,7 @@ export class ComponentRegistry { const typeName = getComponentTypeName(componentType); throw new Error(`Component type ${typeName} is not registered`); } - - const mask: BitMask64Data = { lo: 0, hi: 0 }; - BitMask64Utils.setBitExtended(mask, bitIndex); - return mask; + return BitMask64Utils.create(bitIndex); } /** diff --git a/packages/core/src/ECS/Core/QuerySystem.ts b/packages/core/src/ECS/Core/QuerySystem.ts index 1940647c..f484896e 100644 --- a/packages/core/src/ECS/Core/QuerySystem.ts +++ b/packages/core/src/ECS/Core/QuerySystem.ts @@ -4,9 +4,8 @@ import { ComponentRegistry, ComponentType } from './ComponentStorage'; import { BitMask64Utils, BitMask64Data } from '../Utils/BigIntCompatibility'; import { createLogger } from '../../Utils/Logger'; import { getComponentTypeName } from '../Decorators'; - -import { ComponentPoolManager } from './ComponentPool'; -import { ArchetypeSystem, Archetype, ArchetypeQueryResult } from './ArchetypeSystem'; +import { Archetype, ArchetypeSystem } from './ArchetypeSystem'; +import { ComponentTypeManager } from "../Utils"; /** * 查询条件类型 @@ -787,8 +786,7 @@ export class QuerySystem { private createComponentMask(componentTypes: ComponentType[]): BitMask64Data { // 生成缓存键 const cacheKey = componentTypes.map(t => { - const name = getComponentTypeName(t); - return name; + return getComponentTypeName(t); }).sort().join(','); // 检查缓存 @@ -797,27 +795,10 @@ export class QuerySystem { return cached; } - let mask = BitMask64Utils.clone(BitMask64Utils.ZERO); - let hasValidComponents = false; - - for (const type of componentTypes) { - try { - const bitMask = ComponentRegistry.getBitMask(type); - BitMask64Utils.orInPlace(mask, bitMask); - hasValidComponents = true; - } catch (error) { - this._logger.warn(`组件类型 ${getComponentTypeName(type)} 未注册,跳过`); - } - } - - // 如果没有有效的组件类型,返回一个不可能匹配的掩码 - if (!hasValidComponents) { - mask = { lo: 0xFFFFFFFF, hi: 0xFFFFFFFF }; - } - + let mask = ComponentTypeManager.instance.getEntityBits(componentTypes); // 缓存结果 - this.componentMaskCache.set(cacheKey, mask); - return mask; + this.componentMaskCache.set(cacheKey, mask.getValue()); + return mask.getValue(); } /** diff --git a/packages/core/src/ECS/Entity.ts b/packages/core/src/ECS/Entity.ts index 1de94dbc..7bcd2d27 100644 --- a/packages/core/src/ECS/Entity.ts +++ b/packages/core/src/ECS/Entity.ts @@ -191,7 +191,7 @@ export class Entity { const maxBitIndex = ComponentRegistry.getRegisteredCount(); for (let bitIndex = 0; bitIndex < maxBitIndex; bitIndex++) { - if (BitMask64Utils.getBitExtended(mask, bitIndex)) { + if (BitMask64Utils.getBit(mask, bitIndex)) { const componentType = ComponentRegistry.getTypeByBitIndex(bitIndex); if (componentType) { let component: Component | null = null; @@ -504,7 +504,7 @@ export class Entity { this._localComponents.delete(componentType); // 更新位掩码 - BitMask64Utils.clearBitExtended(this._componentMask, bitIndex); + BitMask64Utils.clearBit(this._componentMask, bitIndex); // 使缓存失效 this._componentCache = null; diff --git a/packages/core/src/ECS/Utils/BigIntCompatibility.ts b/packages/core/src/ECS/Utils/BigIntCompatibility.ts index a75eeb07..f21b19b9 100644 --- a/packages/core/src/ECS/Utils/BigIntCompatibility.ts +++ b/packages/core/src/ECS/Utils/BigIntCompatibility.ts @@ -1,31 +1,31 @@ +/** + * 位枚举 + */ +export enum SegmentPart { + LOW = 0, + HIGH = 1, +} /** * 基础 64 位段结构 + * [低32位,高32位] */ -export interface BitMask64Segment { - /** 低32位(bit 0-31) */ - lo: number; - /** 高32位(bit 32-63) */ - hi: number; -} +export type BitMask64Segment = [number,number] /** * 位掩码数据结构 - * 基础模式(64位):使用 lo + hi 存储 64 位,segments 为空 - * 扩展模式(128+位):lo + hi 作为第一段,segments 存储额外的 64 位段 + * 基础模式(64位):使用 base[lo , hi] 存储 64 位,segments 为空 + * 扩展模式(128+位):base[lo , hi] 作为第一段,segments 存储额外的 64 位段 * segments[0] 对应 bit 64-127,segments[1] 对应 bit 128-191,以此类推 */ export interface BitMask64Data { - /** 低32位(bit 0-31) */ - lo: number; - /** 高32位(bit 32-63) */ - hi: number; + base: BitMask64Segment; /** 扩展段数组,每个元素是一个 64 位段,用于超过 64 位的场景 */ segments?: BitMask64Segment[]; } export class BitMask64Utils { /** 零掩码常量,所有位都为0 */ - public static readonly ZERO: BitMask64Data = { lo: 0, hi: 0 }; + public static readonly ZERO: Readonly = { base: [0, 0], segments: undefined }; /** * 根据位索引创建64位掩码 @@ -34,15 +34,12 @@ export class BitMask64Utils { * @throws 当位索引超出范围时抛出错误 */ public static create(bitIndex: number): BitMask64Data { - if (bitIndex < 0 || bitIndex >= 64) { - throw new Error(`Bit index ${bitIndex} out of range [0, 63]`); - } - - if (bitIndex < 32) { - return { lo: 1 << bitIndex, hi: 0 }; - } else { - return { lo: 0, hi: 1 << (bitIndex - 32) }; + if (bitIndex < 0) { + throw new Error(`Bit index ${bitIndex} out of range [0, ∞)`); } + const mask: BitMask64Data = { base: [0, 0], segments: undefined }; + BitMask64Utils.setBit(mask, bitIndex); + return mask; } /** @@ -51,7 +48,7 @@ export class BitMask64Utils { * @returns 低32位为输入值、高32位为0的掩码 */ public static fromNumber(value: number): BitMask64Data { - return { lo: value >>> 0, hi: 0 }; + return { base: [value >>> 0, 0], segments: undefined}; } /** @@ -61,75 +58,50 @@ export class BitMask64Utils { * @returns 如果掩码包含bits中的任意位则返回true */ public static hasAny(mask: BitMask64Data, bits: BitMask64Data): boolean { - // 检查第一个 64 位段 - if ((mask.lo & bits.lo) !== 0 || (mask.hi & bits.hi) !== 0) { - return true; - } - - // 如果 bits 没有扩展段,检查完成 - if (!bits.segments || bits.segments.length === 0) { - return false; - } - - // 如果 bits 有扩展段但 mask 没有,返回 false - if (!mask.segments || mask.segments.length === 0) { - return false; - } - - // 检查每个扩展段 - const minSegments = Math.min(mask.segments.length, bits.segments.length); - for (let i = 0; i < minSegments; i++) { - const maskSeg = mask.segments[i]; - const bitsSeg = bits.segments[i]; - if ((maskSeg.lo & bitsSeg.lo) !== 0 || (maskSeg.hi & bitsSeg.hi) !== 0) { - return true; - } - } - - return false; + const bitsBase = bits.base; + const maskBase = mask.base; + const bitsSegments = bits.segments; + const maskSegments = mask.segments; + const baseHasAny = (maskBase[SegmentPart.LOW] & bitsBase[SegmentPart.LOW]) !== 0 || (maskBase[SegmentPart.HIGH] & bitsBase[SegmentPart.HIGH]) !== 0; + // 基础区段就包含指定的位,或任意一个参数不含扩展区段,直接短路 + if(baseHasAny || !bitsSegments || !maskSegments) return baseHasAny; + // 额外检查扩展区域是否包含指定的位 - 如果bitsSegments[index]不存在,会被转为NaN,NaN的位运算始终返回0 + return maskSegments.some((seg, index) => (seg[SegmentPart.LOW] & bitsSegments[index][SegmentPart.LOW]) !== 0 || (seg[SegmentPart.HIGH] & bitsSegments[index][SegmentPart.HIGH]) !== 0) } /** * 检查掩码是否包含所有指定的位 - * 支持扩展模式,自动处理超过 64 位的掩码 * @param mask 要检查的掩码 * @param bits 指定的位模式 * @returns 如果掩码包含bits中的所有位则返回true */ public static hasAll(mask: BitMask64Data, bits: BitMask64Data): boolean { - // 检查第一个 64 位段 - if ((mask.lo & bits.lo) !== bits.lo || (mask.hi & bits.hi) !== bits.hi) { - return false; - } + const maskBase = mask.base; + const bitsBase = bits.base; + const maskSegments = mask.segments; + const bitsSegments = bits.segments; + const baseHasAll = (maskBase[SegmentPart.LOW] & bitsBase[SegmentPart.LOW]) === bitsBase[SegmentPart.LOW] && (maskBase[SegmentPart.HIGH] & bitsBase[SegmentPart.HIGH]) === bitsBase[SegmentPart.HIGH]; + // 基础区域就不包含指定的位,或bits没有扩展区段,直接短路。 + if(!baseHasAll || !bitsSegments) return baseHasAll; - // 如果 bits 没有扩展段,检查完成 - if (!bits.segments || bits.segments.length === 0) { - return true; + // 扩展区段的hasAll匹配逻辑 + const maskSegmentsLength = maskSegments?.length ?? 0; + // 对mask/bits中都存在的区段,进行hasAll判断 + if(maskSegments){ + for (let i = 0; i < Math.min(maskSegmentsLength,bitsSegments.length); i++) { + if((maskSegments[i][SegmentPart.LOW] & bitsSegments[i][SegmentPart.LOW]) !== bitsSegments[i][SegmentPart.LOW] || (maskSegments[i][SegmentPart.HIGH] & bitsSegments[i][SegmentPart.HIGH]) !== bitsSegments[i][SegmentPart.HIGH]){ + // 存在不匹配的位,直接短路 + return false; + } + } } - - // 如果 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++) { - const maskSeg = mask.segments[i]; - const bitsSeg = bits.segments[i]; - if ((maskSeg.lo & bitsSeg.lo) !== bitsSeg.lo || (maskSeg.hi & bitsSeg.hi) !== bitsSeg.hi) { + // 对mask中不存在,但bits中存在的区段,进行isZero判断 + for (let i = maskSegmentsLength; i < bitsSegments.length; i++) { + if(bitsSegments[i][SegmentPart.LOW] !== 0 || bitsSegments[i][SegmentPart.HIGH] !== 0){ + // 存在不为0的区段,直接短路 return false; } } - - // 如果 bits 有更多段,检查这些段是否为空 - for (let i = minSegments; i < bits.segments.length; i++) { - if (!BitMask64Utils.isZero(bits.segments[i])) { - return false; - } - } - return true; } @@ -140,7 +112,15 @@ export class BitMask64Utils { * @returns 如果掩码不包含bits中的任何位则返回true */ public static hasNone(mask: BitMask64Data, bits: BitMask64Data): boolean { - return (mask.lo & bits.lo) === 0 && (mask.hi & bits.hi) === 0; + const maskBase = mask.base; + const bitsBase = bits.base; + const maskSegments = mask.segments; + const bitsSegments = bits.segments; + const baseHasNone = (maskBase[SegmentPart.LOW] & bitsBase[SegmentPart.LOW]) === 0 && (maskBase[SegmentPart.HIGH] & bitsBase[SegmentPart.HIGH]) === 0; + //不含扩展区域,或基础区域就包含指定的位,或bits不含拓展区段,直接短路。 + if(!maskSegments || !baseHasNone || !bitsSegments) return baseHasNone; + // 额外检查扩展区域是否都包含指定的位 - 此时bitsSegments存在,如果bitsSegments[index]不存在,会被转为NaN,NaN的位运算始终返回0 + return maskSegments.every((seg, index) => (seg[SegmentPart.LOW] & bitsSegments[index][SegmentPart.LOW]) === 0 && (seg[SegmentPart.HIGH] & bitsSegments[index][SegmentPart.HIGH]) === 0); } /** @@ -148,8 +128,14 @@ export class BitMask64Utils { * @param mask 要检查的掩码 * @returns 如果掩码所有位都为0则返回true */ - public static isZero(mask: BitMask64Data | BitMask64Segment): boolean { - return mask.lo === 0 && mask.hi === 0; + public static isZero(mask: BitMask64Data): boolean { + const baseIsZero = mask.base[SegmentPart.LOW] === 0 && mask.base[SegmentPart.HIGH] === 0; + if(!mask.segments || !baseIsZero){ + // 不含扩展区域,或基础区域值就不为0,直接短路 + return baseIsZero; + } + // 额外检查扩展区域是否都为0 + return mask.segments.every(seg => seg[SegmentPart.LOW] === 0 && seg[SegmentPart.HIGH] === 0); } /** @@ -159,42 +145,82 @@ export class BitMask64Utils { * @returns 如果两个掩码完全相等则返回true */ public static equals(a: BitMask64Data, b: BitMask64Data): boolean { - return a.lo === b.lo && a.hi === b.hi; + let baseEquals = a.base[SegmentPart.LOW] === b.base[SegmentPart.LOW] && a.base[SegmentPart.HIGH] === b.base[SegmentPart.HIGH]; + // base不相等,或ab都没有扩展区域位,直接返回base比较结果 + if(!baseEquals || (!a.segments && !b.segments)) return baseEquals; + // 不能假设a,b的segments都存在或长度相同. + const aSegments = a.segments ?? []; + const bSegments = b.segments ?? []; + for (let i = 0; i < Math.max(aSegments.length, bSegments.length); i++) { + const aSeg: BitMask64Segment | undefined = aSegments[i]; // 可能为undefined + const bSeg: BitMask64Segment | undefined = bSegments[i]; // 可能为undefined + if(aSeg && !bSeg){ + //bSeg不存在,则必须要求aSeg全为0 + if(aSeg[SegmentPart.LOW] !== 0 || aSeg[SegmentPart.HIGH] !== 0) return false; + }else if(!aSeg && bSeg){ + //aSeg不存在,则必须要求bSeg全为0 + if(bSeg[SegmentPart.LOW] !== 0 || bSeg[SegmentPart.HIGH] !== 0) return false; + }else{ + //理想状态:aSeg/bSeg都存在 + if(aSeg[SegmentPart.LOW] !== bSeg[SegmentPart.LOW] || aSeg[SegmentPart.HIGH] !== bSeg[SegmentPart.HIGH]) return false; + } + } + return true; } /** - * 设置掩码中指定位为1 + * 设置掩码中指定位为1,必要时自动扩展 * @param mask 要修改的掩码(原地修改) - * @param bitIndex 位索引,范围 [0, 63] + * @param bitIndex 位索引,不小于零 * @throws 当位索引超出范围时抛出错误 */ public static setBit(mask: BitMask64Data, bitIndex: number): void { - if (bitIndex < 0 || bitIndex >= 64) { + if (bitIndex < 0) { throw new Error(`Bit index ${bitIndex} out of range [0, 63]`); } - - if (bitIndex < 32) { - mask.lo |= (1 << bitIndex); + const targetSeg = BitMask64Utils.getSegmentByBitIndex(mask, bitIndex)!; + const mod = bitIndex & 63; // bitIndex % 64 优化方案 + if(mod < 32){ + targetSeg[SegmentPart.LOW] |= (1 << mod); } else { - mask.hi |= (1 << (bitIndex - 32)); + targetSeg[SegmentPart.HIGH] |= (1 << (mod - 32)); } } /** - * 清除掩码中指定位为0 + * 获取掩码中指定位,如果位超出当前掩码的区段长度,则直接返回0 + * @param mask 掩码 + * @param bitIndex 位索引,不小于零 + */ + public static getBit(mask: BitMask64Data, bitIndex: number): boolean { + if (bitIndex < 0) { + return false; + } + const targetSeg = BitMask64Utils.getSegmentByBitIndex(mask, bitIndex, false); + if(!targetSeg) return false; + const mod = bitIndex & 63; // bitIndex % 64 优化方案 + if(mod < 32){ + return (targetSeg[SegmentPart.LOW] & (1 << mod)) !== 0; + } else { + return (targetSeg[SegmentPart.HIGH] & (1 << (mod - 32))) !== 0; + } + } + /** + * 清除掩码中指定位为0,如果位超出当前掩码的区段长度,则什么也不做 * @param mask 要修改的掩码(原地修改) - * @param bitIndex 位索引,范围 [0, 63] - * @throws 当位索引超出范围时抛出错误 + * @param bitIndex 位索引,不小于0 */ public static clearBit(mask: BitMask64Data, bitIndex: number): void { - if (bitIndex < 0 || bitIndex >= 64) { + if (bitIndex < 0) { throw new Error(`Bit index ${bitIndex} out of range [0, 63]`); } - - if (bitIndex < 32) { - mask.lo &= ~(1 << bitIndex); + const targetSeg = BitMask64Utils.getSegmentByBitIndex(mask, bitIndex, false); + if(!targetSeg) return; + const mod = bitIndex & 63; // bitIndex % 64 优化方案 + if(mod < 32){ + targetSeg[SegmentPart.LOW] &= ~(1 << mod); } else { - mask.hi &= ~(1 << (bitIndex - 32)); + targetSeg[SegmentPart.HIGH] &= ~(1 << (mod - 32)); } } @@ -204,24 +230,26 @@ export class BitMask64Utils { * @param other 用于按位或的掩码 */ public static orInPlace(target: BitMask64Data, other: BitMask64Data): void { - target.lo |= other.lo; - target.hi |= other.hi; + target.base[SegmentPart.LOW] |= other.base[SegmentPart.LOW]; + target.base[SegmentPart.HIGH] |= other.base[SegmentPart.HIGH]; // 处理扩展段 - if (other.segments && other.segments.length > 0) { + const otherSegments = other.segments; + if (otherSegments && otherSegments.length > 0) { if (!target.segments) { target.segments = []; } + const targetSegments = target.segments; // 确保 target 有足够的段 - while (target.segments.length < other.segments.length) { - target.segments.push({ lo: 0, hi: 0 }); + while (targetSegments.length < otherSegments.length) { + targetSegments.push([0, 0]); } // 对每个段执行或操作 - for (let i = 0; i < other.segments.length; i++) { - target.segments[i].lo |= other.segments[i].lo; - target.segments[i].hi |= other.segments[i].hi; + for (let i = 0; i < otherSegments.length; i++) { + targetSegments[i][SegmentPart.LOW] |= otherSegments[i][SegmentPart.LOW]; + targetSegments[i][SegmentPart.HIGH] |= otherSegments[i][SegmentPart.HIGH]; } } } @@ -232,8 +260,27 @@ export class BitMask64Utils { * @param other 用于按位与的掩码 */ public static andInPlace(target: BitMask64Data, other: BitMask64Data): void { - target.lo &= other.lo; - target.hi &= other.hi; + target.base[SegmentPart.LOW] &= other.base[SegmentPart.LOW]; + target.base[SegmentPart.HIGH] &= other.base[SegmentPart.HIGH]; + + // 处理扩展段 + const otherSegments = other.segments; + if (otherSegments && otherSegments.length > 0) { + if (!target.segments) { + target.segments = []; + } + const targetSegments = target.segments; + // 确保 target 有足够的段 + while (targetSegments.length < otherSegments.length) { + targetSegments.push([0, 0]); + } + + // 对每个段执行与操作 + for (let i = 0; i < otherSegments.length; i++) { + targetSegments[i][SegmentPart.LOW] &= otherSegments[i][SegmentPart.LOW]; + targetSegments[i][SegmentPart.HIGH] &= otherSegments[i][SegmentPart.HIGH]; + } + } } /** @@ -242,8 +289,25 @@ export class BitMask64Utils { * @param other 用于按位异或的掩码 */ public static xorInPlace(target: BitMask64Data, other: BitMask64Data): void { - target.lo ^= other.lo; - target.hi ^= other.hi; + target.base[SegmentPart.LOW] ^= other.base[SegmentPart.LOW]; + target.base[SegmentPart.HIGH] ^= other.base[SegmentPart.HIGH]; + + // 处理扩展段 + const otherSegments = other.segments; + if (!otherSegments || otherSegments.length == 0) return; + if (!target.segments) target.segments = []; + + const targetSegments = target.segments; + // 确保 target 有足够的段 + while (targetSegments.length < otherSegments.length) { + targetSegments.push([0, 0]); + } + + // 对每个段执行异或操作 + for (let i = 0; i < otherSegments.length; i++) { + targetSegments[i][SegmentPart.LOW] ^= otherSegments[i][SegmentPart.LOW]; + targetSegments[i][SegmentPart.HIGH] ^= otherSegments[i][SegmentPart.HIGH]; + } } /** @@ -251,18 +315,43 @@ export class BitMask64Utils { * @param mask 要清除的掩码(原地修改) */ public static clear(mask: BitMask64Data): void { - mask.lo = 0; - mask.hi = 0; + mask.base[SegmentPart.LOW] = 0; + mask.base[SegmentPart.HIGH] = 0; + for (let i = 0; i < (mask.segments?.length ?? 0); i++) { + const seg = mask.segments![i]; + seg[SegmentPart.LOW] = 0; + seg[SegmentPart.HIGH] = 0; + } } /** - * 将源掩码的值复制到目标掩码 + * 将源掩码的值复制到目标掩码,如果source包含扩展段,则target也会至少扩展到source扩展段的长度 * @param source 源掩码 * @param target 目标掩码(原地修改) */ public static copy(source: BitMask64Data, target: BitMask64Data): void { - target.lo = source.lo; - target.hi = source.hi; + BitMask64Utils.clear(target); + target.base[SegmentPart.LOW] = source.base[SegmentPart.LOW]; + target.base[SegmentPart.HIGH] = source.base[SegmentPart.HIGH]; + // source没有扩展段,直接退出 + if(!source.segments || source.segments.length == 0) return; + // 没有拓展段,则直接复制数组 + if(!target.segments){ + target.segments = source.segments.map(seg => [...seg]); + return; + } + // source有扩展段,target扩展段不足,则补充长度 + const copyLength = source.segments.length - target.segments.length; + for (let i = 0; i < copyLength; i++) { + target.segments.push([0,0]); + } + // 逐个重写 + for (let i = 0; i < length; i++) { + const targetSeg = target.segments![i]; + const sourSeg = source.segments![i]; + targetSeg[SegmentPart.LOW] = sourSeg[SegmentPart.LOW]; + targetSeg[SegmentPart.HIGH] = sourSeg[SegmentPart.HIGH]; + } } /** @@ -271,36 +360,57 @@ export class BitMask64Utils { * @returns 新的掩码对象,内容与源掩码相同 */ public static clone(mask: BitMask64Data): BitMask64Data { - return { lo: mask.lo, hi: mask.hi }; + return { + base: mask.base.slice() as BitMask64Segment, + segments: mask.segments ? mask.segments.map(seg => [...seg]) : undefined + }; } /** - * 将掩码转换为字符串表示 + * 将掩码转换为字符串表示,每个区段之间将使用空格分割。 * @param mask 要转换的掩码 - * @param radix 进制,支持2(二进制)或16(十六进制),默认为2 + * @param radix 进制,支持2(二进制)或16(十六进制),默认为2,其他的值被视为2 + * @param printHead 打印头 * @returns 掩码的字符串表示,二进制不带前缀,十六进制带0x前缀 - * @throws 当进制不支持时抛出错误 */ - public static toString(mask: BitMask64Data, radix: number = 2): string { - if (radix === 2) { - if (mask.hi === 0) { - return mask.lo.toString(2); - } else { - const hiBits = mask.hi.toString(2); - const loBits = mask.lo.toString(2).padStart(32, '0'); - return hiBits + loBits; + public static toString(mask: BitMask64Data, radix: 2 | 16 = 2, printHead: boolean = false): string { + if(radix != 2 && radix != 16) radix = 2; + const totalLength = mask.segments?.length ?? 0; + let result: string = ''; + if(printHead){ + let paddingLength = 0; + if(radix === 2){ + paddingLength = 64 + 1 + 1; + }else{ + paddingLength = 16 + 2 + 1; } - } else if (radix === 16) { - if (mask.hi === 0) { - return '0x' + mask.lo.toString(16).toUpperCase(); - } else { - const hiBits = mask.hi.toString(16).toUpperCase(); - const loBits = mask.lo.toString(16).toUpperCase().padStart(8, '0'); - return '0x' + hiBits + loBits; + for (let i = 0; i <= totalLength; i++) { + const title = i === 0 ? '0 (Base):' : `${i} (${64 * i}):`; + result += title.toString().padEnd(paddingLength); } - } else { - throw new Error('Only radix 2 and 16 are supported'); + result += '\n'; } + + for (let i = -1; i < totalLength; i++) { + let segResult = ''; + const bitMaskData = i == -1 ? mask.base : mask.segments![i]; + let hi = bitMaskData[SegmentPart.HIGH]; + let lo = bitMaskData[SegmentPart.LOW]; + if(radix == 2){ + const hiBits = hi.toString(2).padStart(32, '0'); + const loBits = lo.toString(2).padStart(32, '0'); + segResult = hiBits + '_' + loBits; //高低位之间使用_隔离 + }else{ + const hiBits = hi.toString(16).toUpperCase().padStart(8, '0'); + const loBits = lo.toString(16).toUpperCase().padStart(8, '0'); + segResult = '0x' + hiBits + loBits; + } + if(i === -1) + result += segResult; + else + result += ' ' + segResult; // 不同段之间使用空格隔离 + } + return result; } /** @@ -310,126 +420,49 @@ export class BitMask64Utils { */ public static popCount(mask: BitMask64Data): number { let count = 0; - let lo = mask.lo; - let hi = mask.hi; - - while (lo) { - lo &= lo - 1; - count++; + for (let i = -1; i < (mask.segments?.length ?? 0); i++) { + const bitMaskData = i == -1 ? mask.base : mask.segments![i]; + let lo = bitMaskData[SegmentPart.LOW]; + let hi = bitMaskData[SegmentPart.HIGH]; + while (lo) { + lo &= lo - 1; + count++; + } + while (hi) { + hi &= hi - 1; + count++; + } } - - while (hi) { - hi &= hi - 1; - count++; - } - return count; } /** - * 设置扩展位(支持超过 64 位的索引) - * @param mask 要修改的掩码(原地修改) - * @param bitIndex 位索引(可以超过 63) + * 获取包含目标位的BitMask64Segment + * @param mask 要操作的掩码 + * @param bitIndex 目标位 + * @param createNewSegment 如果bitIndex超过了当前范围,是否自动补充扩展区域,默认为真 + * @private */ - 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({ lo: 0, hi: 0 }); - } - - // 设置对应段的位 - const segment = mask.segments[segmentIndex]; - if (localBitIndex < 32) { - segment.lo |= (1 << localBitIndex); - } else { - segment.hi |= (1 << (localBitIndex - 32)); - } - } - - /** - * 获取扩展位(支持超过 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 segment = mask.segments[segmentIndex]; - - if (localBitIndex < 32) { - return (segment.lo & (1 << localBitIndex)) !== 0; - } else { - return (segment.hi & (1 << (localBitIndex - 32))) !== 0; - } - } - - /** - * 清除扩展位(支持超过 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; - const segment = mask.segments[segmentIndex]; - - if (localBitIndex < 32) { - segment.lo &= ~(1 << localBitIndex); - } else { - segment.hi &= ~(1 << (localBitIndex - 32)); + private static getSegmentByBitIndex(mask: BitMask64Data,bitIndex: number, createNewSegment: boolean = true): BitMask64Segment | null{ + if(bitIndex <= 63){ + // 基础位 + return mask.base; + }else{ + // 扩展位 + let segments = mask.segments; + if(!segments) { + if(!createNewSegment) return null; + segments = mask.segments = []; + } + const targetSegIndex = (bitIndex >> 6) - 1; // Math.floor(bitIndex / 64) - 1的位运算优化 + if(segments.length <= targetSegIndex){ + if(!createNewSegment) return null; + const diff = targetSegIndex - segments.length + 1; + for (let i = 0; i < diff; i++) { + segments.push([0, 0]); + } + } + return segments[targetSegIndex]; } } } \ No newline at end of file diff --git a/packages/core/src/ECS/Utils/Bits.ts b/packages/core/src/ECS/Utils/Bits.ts index b725b740..7d7e96fa 100644 --- a/packages/core/src/ECS/Utils/Bits.ts +++ b/packages/core/src/ECS/Utils/Bits.ts @@ -1,9 +1,8 @@ -import { BitMask64Data, BitMask64Utils } from './BigIntCompatibility'; +import { SegmentPart, BitMask64Data, BitMask64Utils } from './BigIntCompatibility'; /** * 位集合类,用于高效的位操作 - * 自动扩展支持:默认 64 位,超过时自动扩展到 128/256 位 - * 扩展模式性能略有下降,但仍然比数组遍历快得多 + * 支持任意位的位运算操作. */ export class Bits { /** 存储位数据的掩码,支持扩展 */ @@ -37,7 +36,7 @@ export class Bits { throw new Error('Bit index cannot be negative'); } - BitMask64Utils.setBitExtended(this._value, index); + BitMask64Utils.setBit(this._value, index); } /** @@ -50,7 +49,7 @@ export class Bits { throw new Error('Bit index cannot be negative'); } - BitMask64Utils.clearBitExtended(this._value, index); + BitMask64Utils.clearBit(this._value, index); } /** @@ -59,7 +58,7 @@ export class Bits { * @returns 如果位被设置为1则返回true,否则返回false */ public get(index: number): boolean { - return BitMask64Utils.getBitExtended(this._value, index); + return BitMask64Utils.getBit(this._value, index); } /** @@ -163,16 +162,16 @@ export class Bits { if (maxBits <= 32) { const mask = (1 << maxBits) - 1; - result._value.lo = (~result._value.lo) & mask; - result._value.hi = 0; + result._value.base[SegmentPart.LOW] = (~result._value.base[SegmentPart.LOW]) & mask; + result._value.base[SegmentPart.HIGH] = 0; } else { - result._value.lo = ~result._value.lo; + result._value.base[SegmentPart.LOW] = ~result._value.base[SegmentPart.LOW]; if (maxBits < 64) { const remainingBits = maxBits - 32; const mask = (1 << remainingBits) - 1; - result._value.hi = (~result._value.hi) & mask; + result._value.base[SegmentPart.HIGH] = (~result._value.base[SegmentPart.HIGH]) & mask; } else { - result._value.hi = ~result._value.hi; + result._value.base[SegmentPart.HIGH] = ~result._value.base[SegmentPart.HIGH]; } } @@ -237,9 +236,10 @@ export class Bits { * @param maxBits 最大位数,默认为64 * @returns 二进制字符串表示,每8位用空格分隔 */ - public toBinaryString(maxBits: number = 64): string { - if (maxBits > 64) maxBits = 64; - + public toBinaryString(maxBits: number = 0): string { + if(maxBits == 0){ + maxBits = 64 + (this._value.segments ? this._value.segments.length * 64 : 0); + } let result = ''; for (let i = maxBits - 1; i >= 0; i--) { result += this.get(i) ? '1' : '0'; @@ -265,16 +265,16 @@ export class Bits { */ public static fromBinaryString(binaryString: string): Bits { const cleanString = binaryString.replace(/\s/g, ''); - let data: BitMask64Data; + let data: BitMask64Data = { base: undefined!, segments: undefined}; if (cleanString.length <= 32) { const num = parseInt(cleanString, 2); - data = { lo: num >>> 0, hi: 0 }; + data.base = [num >>> 0, 0]; } else { const loBits = cleanString.substring(cleanString.length - 32); const hiBits = cleanString.substring(0, cleanString.length - 32); const lo = parseInt(loBits, 2); const hi = parseInt(hiBits, 2); - data = { lo: lo >>> 0, hi: hi >>> 0 }; + data.base = [lo >>> 0, hi >>> 0]; } return new Bits(data); } @@ -286,16 +286,16 @@ export class Bits { */ public static fromHexString(hexString: string): Bits { const cleanString = hexString.replace(/^0x/i, ''); - let data: BitMask64Data; + let data: BitMask64Data = { base: undefined!, segments: undefined}; if (cleanString.length <= 8) { const num = parseInt(cleanString, 16); - data = { lo: num >>> 0, hi: 0 }; + data.base = [num >>> 0, 0]; } else { const loBits = cleanString.substring(cleanString.length - 8); const hiBits = cleanString.substring(0, cleanString.length - 8); const lo = parseInt(loBits, 16); const hi = parseInt(hiBits, 16); - data = { lo: lo >>> 0, hi: hi >>> 0 }; + data.base = [lo >>> 0, hi >>> 0]; } return new Bits(data); } @@ -318,16 +318,16 @@ export class Bits { return -1; } - if (this._value.hi !== 0) { + if (this._value.base[SegmentPart.HIGH] !== 0) { for (let i = 31; i >= 0; i--) { - if ((this._value.hi & (1 << i)) !== 0) { + if ((this._value.base[SegmentPart.HIGH] & (1 << i)) !== 0) { return i + 32; } } } for (let i = 31; i >= 0; i--) { - if ((this._value.lo & (1 << i)) !== 0) { + if ((this._value.base[SegmentPart.LOW] & (1 << i)) !== 0) { return i; } } @@ -345,13 +345,13 @@ export class Bits { } for (let i = 0; i < 32; i++) { - if ((this._value.lo & (1 << i)) !== 0) { + if ((this._value.base[SegmentPart.LOW] & (1 << i)) !== 0) { return i; } } for (let i = 0; i < 32; i++) { - if ((this._value.hi & (1 << i)) !== 0) { + if ((this._value.base[SegmentPart.HIGH] & (1 << i)) !== 0) { return i + 32; } } diff --git a/packages/core/tests/ECS/Utils/BigIntCompatibility.test.ts b/packages/core/tests/ECS/Utils/BigIntCompatibility.test.ts index 23e38092..0bc3e6a5 100644 --- a/packages/core/tests/ECS/Utils/BigIntCompatibility.test.ts +++ b/packages/core/tests/ECS/Utils/BigIntCompatibility.test.ts @@ -1,247 +1,189 @@ -import { - BitMask64Data, - BitMask64Utils -} from '../../../src/ECS/Utils/BigIntCompatibility'; +import { BitMask64Data, BitMask64Utils } from "../../../src"; +import { SegmentPart } from "../../../src/ECS/Utils/BigIntCompatibility"; -describe('64位掩码兼容性测试', () => { - describe('基本功能', () => { - it('应该能够创建和检查掩码', () => { - const zero = BitMask64Utils.ZERO; - const mask1 = BitMask64Utils.create(0); - const mask2 = BitMask64Utils.create(5); - - expect(BitMask64Utils.isZero(zero)).toBe(true); - expect(BitMask64Utils.isZero(mask1)).toBe(false); - expect(BitMask64Utils.isZero(mask2)).toBe(false); - }); +describe("BitMask64Utils 位掩码工具测试", () => { + test("create() 应该在指定索引位置设置位", () => { + const mask = BitMask64Utils.create(0); + expect(mask.base[0]).toBe(1); + expect(mask.base[1]).toBe(0); - it('应该支持数字创建', () => { - const mask = BitMask64Utils.fromNumber(42); - expect(mask.lo).toBe(42); - expect(mask.hi).toBe(0); - }); + const mask2 = BitMask64Utils.create(33); + expect(mask2.base[0]).toBe(0); + expect(mask2.base[1]).toBe(0b10); }); - describe('位运算', () => { - let mask1: BitMask64Data; - let mask2: BitMask64Data; - - beforeEach(() => { - mask1 = BitMask64Utils.create(2); // 位2 - mask2 = BitMask64Utils.create(3); // 位3 - }); - - it('hasAny运算', () => { - const combined = BitMask64Utils.clone(BitMask64Utils.ZERO); - BitMask64Utils.orInPlace(combined, mask1); - BitMask64Utils.orInPlace(combined, mask2); - - expect(BitMask64Utils.hasAny(combined, mask1)).toBe(true); - expect(BitMask64Utils.hasAny(combined, mask2)).toBe(true); - - const mask4 = BitMask64Utils.create(4); - expect(BitMask64Utils.hasAny(combined, mask4)).toBe(false); - }); - - it('hasAll运算', () => { - const combined = BitMask64Utils.clone(BitMask64Utils.ZERO); - BitMask64Utils.orInPlace(combined, mask1); - BitMask64Utils.orInPlace(combined, mask2); - - expect(BitMask64Utils.hasAll(combined, mask1)).toBe(true); - expect(BitMask64Utils.hasAll(combined, mask2)).toBe(true); - - const both = BitMask64Utils.clone(BitMask64Utils.ZERO); - BitMask64Utils.orInPlace(both, mask1); - BitMask64Utils.orInPlace(both, mask2); - expect(BitMask64Utils.hasAll(combined, both)).toBe(true); - }); - - it('hasNone运算', () => { - const mask4 = BitMask64Utils.create(4); - const mask5 = BitMask64Utils.create(5); - - expect(BitMask64Utils.hasNone(mask1, mask2)).toBe(true); - expect(BitMask64Utils.hasNone(mask1, mask4)).toBe(true); - expect(BitMask64Utils.hasNone(mask1, mask1)).toBe(false); - }); - - it('原地位运算', () => { - const target = BitMask64Utils.clone(mask1); - - // OR操作 - BitMask64Utils.orInPlace(target, mask2); - expect(BitMask64Utils.hasAll(target, mask1)).toBe(true); - expect(BitMask64Utils.hasAll(target, mask2)).toBe(true); - - // AND操作 - const andTarget = BitMask64Utils.clone(target); - BitMask64Utils.andInPlace(andTarget, mask1); - expect(BitMask64Utils.hasAll(andTarget, mask1)).toBe(true); - expect(BitMask64Utils.hasAny(andTarget, mask2)).toBe(false); - - // XOR操作 - const xorTarget = BitMask64Utils.clone(target); - BitMask64Utils.xorInPlace(xorTarget, mask1); - expect(BitMask64Utils.hasAny(xorTarget, mask1)).toBe(false); - expect(BitMask64Utils.hasAll(xorTarget, mask2)).toBe(true); - }); - - it('设置和清除位', () => { - const mask = BitMask64Utils.clone(BitMask64Utils.ZERO); - - BitMask64Utils.setBit(mask, 5); - expect(BitMask64Utils.hasAny(mask, BitMask64Utils.create(5))).toBe(true); - - BitMask64Utils.clearBit(mask, 5); - expect(BitMask64Utils.isZero(mask)).toBe(true); - }); + test("fromNumber() 应该把数值放入低32位", () => { + const mask = BitMask64Utils.fromNumber(123456); + expect(mask.base[0]).toBe(123456); + expect(mask.base[1]).toBe(0); }); - describe('字符串表示', () => { - it('二进制字符串', () => { - const mask = BitMask64Utils.create(5); // 位5设置为1 - const binaryStr = BitMask64Utils.toString(mask, 2); - expect(binaryStr).toBe('100000'); // 位5为1 - }); + test("setBit/getBit/clearBit 应该正确设置、读取和清除位", () => { + const mask: BitMask64Data = { base: [0, 0] }; - it('十六进制字符串', () => { - const mask = BitMask64Utils.fromNumber(255); - const hexStr = BitMask64Utils.toString(mask, 16); - expect(hexStr).toBe('0xFF'); - }); + BitMask64Utils.setBit(mask, 5); + expect(BitMask64Utils.getBit(mask, 5)).toBe(true); - it('大数字的十六进制表示', () => { - const mask: BitMask64Data = { lo: 0xFFFFFFFF, hi: 0x12345678 }; - const hexStr = BitMask64Utils.toString(mask, 16); - expect(hexStr).toBe('0x12345678FFFFFFFF'); - }); + BitMask64Utils.clearBit(mask, 5); + expect(BitMask64Utils.getBit(mask, 5)).toBe(false); + + // 测试扩展段 + BitMask64Utils.setBit(mask, 70); + expect(mask.segments).toBeDefined(); + expect(BitMask64Utils.getBit(mask, 70)).toBe(true); }); - describe('位计数', () => { - it('popCount应该正确计算设置位的数量', () => { - const mask = BitMask64Utils.clone(BitMask64Utils.ZERO); - expect(BitMask64Utils.popCount(mask)).toBe(0); - - BitMask64Utils.setBit(mask, 0); - BitMask64Utils.setBit(mask, 2); - BitMask64Utils.setBit(mask, 4); - expect(BitMask64Utils.popCount(mask)).toBe(3); - }); + test("hasAny/hasAll/hasNone 判断应正确", () => { + const maskA = BitMask64Utils.create(1); + const maskB = BitMask64Utils.create(1); + const maskC = BitMask64Utils.create(2); - it('大数的popCount', () => { - const mask = BitMask64Utils.fromNumber(0xFF); // 8个1 - expect(BitMask64Utils.popCount(mask)).toBe(8); - }); + expect(BitMask64Utils.hasAny(maskA, maskB)).toBe(true); + expect(BitMask64Utils.hasAll(maskA, maskB)).toBe(true); + expect(BitMask64Utils.hasNone(maskA, maskC)).toBe(true); }); - describe('ECS组件掩码操作', () => { - it('多组件掩码组合', () => { - const componentMasks: BitMask64Data[] = []; - for (let i = 0; i < 10; i++) { - componentMasks.push(BitMask64Utils.create(i)); - } - - let combinedMask = BitMask64Utils.clone(BitMask64Utils.ZERO); - for (const mask of componentMasks) { - BitMask64Utils.orInPlace(combinedMask, mask); - } - - expect(BitMask64Utils.popCount(combinedMask)).toBe(10); - - // 检查所有位都设置了 - for (let i = 0; i < 10; i++) { - expect(BitMask64Utils.hasAny(combinedMask, BitMask64Utils.create(i))).toBe(true); - } - }); + test("isZero 应正确判断", () => { + const mask = BitMask64Utils.create(3); + expect(BitMask64Utils.isZero(mask)).toBe(false); - it('实体匹配模拟', () => { - // 模拟实体具有组件0, 2, 4 - const entityMask = BitMask64Utils.clone(BitMask64Utils.ZERO); - BitMask64Utils.setBit(entityMask, 0); - BitMask64Utils.setBit(entityMask, 2); - BitMask64Utils.setBit(entityMask, 4); - - // 查询需要组件0和2 - const queryMask = BitMask64Utils.clone(BitMask64Utils.ZERO); - BitMask64Utils.setBit(queryMask, 0); - BitMask64Utils.setBit(queryMask, 2); - - expect(BitMask64Utils.hasAll(entityMask, queryMask)).toBe(true); - - // 查询需要组件1和3 - const queryMask2 = BitMask64Utils.clone(BitMask64Utils.ZERO); - BitMask64Utils.setBit(queryMask2, 1); - BitMask64Utils.setBit(queryMask2, 3); - - expect(BitMask64Utils.hasAll(entityMask, queryMask2)).toBe(false); - }); + BitMask64Utils.clear(mask); + expect(BitMask64Utils.isZero(mask)).toBe(true); }); - describe('边界测试', () => { - it('应该处理64位边界', () => { - expect(() => BitMask64Utils.create(63)).not.toThrow(); - expect(() => BitMask64Utils.create(64)).toThrow(); - expect(() => BitMask64Utils.create(-1)).toThrow(); - }); + test("equals 应正确判断两个掩码是否相等", () => { + const mask1 = BitMask64Utils.create(10); + const mask2 = BitMask64Utils.create(10); + const mask3 = BitMask64Utils.create(11); - it('设置和清除边界位', () => { - const mask = BitMask64Utils.clone(BitMask64Utils.ZERO); - - BitMask64Utils.setBit(mask, 63); - expect(BitMask64Utils.hasAny(mask, BitMask64Utils.create(63))).toBe(true); - expect(mask.hi).not.toBe(0); - expect(mask.lo).toBe(0); - - BitMask64Utils.clearBit(mask, 63); - expect(BitMask64Utils.isZero(mask)).toBe(true); - }); - - it('高32位和低32位操作', () => { - const lowMask = BitMask64Utils.create(15); // 低32位 - const highMask = BitMask64Utils.create(47); // 高32位 - - expect(lowMask.hi).toBe(0); - expect(lowMask.lo).not.toBe(0); - - expect(highMask.hi).not.toBe(0); - expect(highMask.lo).toBe(0); - - const combined = BitMask64Utils.clone(BitMask64Utils.ZERO); - BitMask64Utils.orInPlace(combined, lowMask); - BitMask64Utils.orInPlace(combined, highMask); - - expect(combined.hi).not.toBe(0); - expect(combined.lo).not.toBe(0); - expect(BitMask64Utils.popCount(combined)).toBe(2); - }); + expect(BitMask64Utils.equals(mask1, mask2)).toBe(true); + expect(BitMask64Utils.equals(mask1, mask3)).toBe(false); }); - describe('复制和相等性', () => { - it('clone应该创建独立副本', () => { - const original = BitMask64Utils.create(5); - const cloned = BitMask64Utils.clone(original); - - expect(BitMask64Utils.equals(original, cloned)).toBe(true); - - BitMask64Utils.setBit(cloned, 6); - expect(BitMask64Utils.equals(original, cloned)).toBe(false); - }); + test("orInPlace/andInPlace/xorInPlace 运算应正确", () => { + const mask1 = BitMask64Utils.create(1); + const mask2 = BitMask64Utils.create(2); - it('copy应该正确复制', () => { - const source = BitMask64Utils.create(10); - const target = BitMask64Utils.clone(BitMask64Utils.ZERO); - - BitMask64Utils.copy(source, target); - expect(BitMask64Utils.equals(source, target)).toBe(true); - }); + BitMask64Utils.orInPlace(mask1, mask2); + expect(BitMask64Utils.getBit(mask1, 1)).toBe(true); + expect(BitMask64Utils.getBit(mask1, 2)).toBe(true); - it('clear应该清除所有位', () => { - const mask = BitMask64Utils.create(20); - expect(BitMask64Utils.isZero(mask)).toBe(false); - - BitMask64Utils.clear(mask); - expect(BitMask64Utils.isZero(mask)).toBe(true); - }); + BitMask64Utils.andInPlace(mask1, mask2); + expect(BitMask64Utils.getBit(mask1, 1)).toBe(false); + expect(BitMask64Utils.getBit(mask1, 2)).toBe(true); + + BitMask64Utils.xorInPlace(mask1, mask2); + expect(BitMask64Utils.getBit(mask1, 2)).toBe(false); }); -}); \ No newline at end of file + + test("copy/clone 应正确复制数据", () => { + const source = BitMask64Utils.create(15); + const target: BitMask64Data = { base: [0, 0] }; + + BitMask64Utils.copy(source, target); + expect(BitMask64Utils.equals(source, target)).toBe(true); + + const clone = BitMask64Utils.clone(source); + expect(BitMask64Utils.equals(source, clone)).toBe(true); + expect(clone).not.toBe(source); // 深拷贝 + }); + + test("toString 应正确输出二进制和十六进制", () => { + const mask = BitMask64Utils.create(0); + expect(BitMask64Utils.toString(mask, 2)).toContain("1"); + expect(BitMask64Utils.toString(mask, 16)).toMatch(/0x/i); + }); + + test("popCount 应返回正确的位数", () => { + const mask = BitMask64Utils.create(0); + BitMask64Utils.setBit(mask, 1); + BitMask64Utils.setBit(mask, 63); + expect(BitMask64Utils.popCount(mask)).toBe(3); + }); + + test("越界与非法输入处理", () => { + expect(() => BitMask64Utils.create(-1)).toThrow(); + expect(BitMask64Utils.getBit({ base: [0,0] }, -5)).toBe(false); + expect(() => BitMask64Utils.clearBit({ base: [0,0] }, -2)).toThrow(); + }); + + test("大于64位的扩展段逻辑 - hasAny/hasAll/hasNone/equals", () => { + // 掩码 A: 只在 bit 150 位置为 1 + const maskA = BitMask64Utils.create(150); + // 掩码 B: 只在 bit 200 位置为 1 + const maskB = BitMask64Utils.create(200); + + // A 与 B 在不同扩展段,不存在重叠位 + expect(BitMask64Utils.hasAny(maskA, maskB)).toBe(false); + expect(BitMask64Utils.hasNone(maskA, maskB)).toBe(true); + + // C: 在 150 与 200 都置位 + const maskC = BitMask64Utils.clone(maskA); + BitMask64Utils.setBit(maskC, 200); + + // A 是 C 的子集 + expect(BitMask64Utils.hasAll(maskC, maskA)).toBe(true); + // B 是 C 的子集 + expect(BitMask64Utils.hasAll(maskC, maskB)).toBe(true); + + // A 和 C 不相等 + expect(BitMask64Utils.equals(maskA, maskC)).toBe(false); + + // C 与自身相等 + expect(BitMask64Utils.equals(maskC, maskC)).toBe(true); + + //copy + const copyMask = BitMask64Utils.create(0); + BitMask64Utils.copy(maskA,copyMask); + expect(BitMask64Utils.equals(copyMask,maskA)).toBe(true); + + // hasAll短路测试,对第一个if的测试覆盖 + BitMask64Utils.setBit(copyMask,64); + expect(BitMask64Utils.hasAll(maskA, copyMask)).toBe(false); + BitMask64Utils.clearBit(copyMask, 64); + + // 扩展到350位,对最后一个短路if的测试覆盖 + BitMask64Utils.setBit(copyMask,350); + expect(BitMask64Utils.hasAll(maskA, copyMask)).toBe(false); + }); + + test("大于64位的逻辑运算 - or/and/xor 跨段处理", () => { + const maskA = BitMask64Utils.create(128); // 第一扩展段 + const maskB = BitMask64Utils.create(190); // 同一扩展段但不同位置 + const maskC = BitMask64Utils.create(300); // 不同扩展段 + + // OR: 合并不同扩展段 + const orMask = BitMask64Utils.clone(maskA); + BitMask64Utils.orInPlace(orMask, maskC); + expect(BitMask64Utils.getBit(orMask, 128)).toBe(true); + expect(BitMask64Utils.getBit(orMask, 300)).toBe(true); + + // AND: 交集为空 + const andMask = BitMask64Utils.clone(maskA); + BitMask64Utils.andInPlace(andMask, maskB); + expect(BitMask64Utils.isZero(andMask)).toBe(true); + + // XOR: 不同扩展段应该都保留 + const xorMask = BitMask64Utils.clone(maskA); + BitMask64Utils.xorInPlace(xorMask, maskC); + expect(BitMask64Utils.getBit(xorMask, 128)).toBe(true); + expect(BitMask64Utils.getBit(xorMask, 300)).toBe(true); + }); + + test("扩展段 toString 与 popCount 验证", () => { + const mask = BitMask64Utils.create(130); // 扩展段,此时扩展段自动延长到2 + BitMask64Utils.setBit(mask, 260); // 再设置另一个更高位,此时扩展段自动延长到3 + + const strBin = BitMask64Utils.toString(mask, 2); + const strHex = BitMask64Utils.toString(mask, 16); + // 第三个区段应该以100结尾(130位为1) + expect(strBin.split(' ')[2].endsWith('100')).toBe(true); + // 第三个区段应该以4结尾(低32位为0x4) + expect(strHex.split(' ')[2].endsWith('4')).toBe(true); + + // 应该有两个置位 + expect(BitMask64Utils.popCount(mask)).toBe(2); + }); + +}); +