From 4a5c890121ab3fb5bdc832108c3a46036e75df95 Mon Sep 17 00:00:00 2001 From: YHH <359807859@qq.com> Date: Wed, 30 Jul 2025 11:11:46 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=B9bigint=E8=BF=9B=E8=A1=8C=E5=85=BC?= =?UTF-8?q?=E5=AE=B9=E5=A4=84=E7=90=86=EF=BC=88=E4=B8=8D=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E7=9A=84=E7=8E=AF=E5=A2=83=E5=9B=9E=E9=80=80=E5=88=B0=E5=85=BC?= =?UTF-8?q?=E5=AE=B9=E6=A8=A1=E5=BC=8F=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Core.ts | 50 ++ src/ECS/Core/BitMaskOptimizer.ts | 75 +- src/ECS/Core/ComponentStorage.ts | 12 +- src/ECS/Core/QuerySystem.ts | 35 +- src/ECS/Entity.ts | 17 +- src/ECS/Utils/BigIntCompatibility.ts | 738 ++++++++++++++++++++ src/ECS/Utils/Bits.ts | 154 ++-- tests/ECS/Core/BitMaskOptimizer.test.ts | 23 +- tests/ECS/Core/ComponentStorage.test.ts | 16 +- tests/ECS/Utils/BigIntCompatibility.test.ts | 338 +++++++++ tests/ECS/Utils/Bits.test.ts | 63 +- 11 files changed, 1376 insertions(+), 145 deletions(-) create mode 100644 src/ECS/Utils/BigIntCompatibility.ts create mode 100644 tests/ECS/Utils/BigIntCompatibility.test.ts diff --git a/src/Core.ts b/src/Core.ts index 66702bae..df0c7560 100644 --- a/src/Core.ts +++ b/src/Core.ts @@ -8,6 +8,7 @@ import { ECSFluentAPI, createECSAPI } from './ECS/Core/FluentAPI'; import { Scene } from './ECS/Scene'; import { DebugManager } from './Utils/Debug'; import { ICoreConfig, IECSDebugConfig } from './Types'; +import { BigIntFactory, EnvironmentInfo } from './ECS/Utils/BigIntCompatibility'; /** * 游戏引擎核心类 @@ -121,6 +122,11 @@ export class Core { */ private _config: ICoreConfig; + /** + * 兼容性信息 + */ + private _environmentInfo: EnvironmentInfo; + /** * 创建核心实例 * @@ -136,6 +142,9 @@ export class Core { ...config }; + // 检测环境兼容性 + this._environmentInfo = BigIntFactory.getEnvironmentInfo(); + // 初始化管理器 this._timerManager = new TimerManager(); Core.registerGlobalManager(this._timerManager); @@ -159,6 +168,11 @@ export class Core { this._debugManager = new DebugManager(this, this._config.debugConfig); } + // 在调试模式下显示兼容性信息 + if (this._config.debug) { + this.logCompatibilityInfo(); + } + this.initialize(); } @@ -379,6 +393,24 @@ export class Core { return this._instance?._config.debugConfig?.enabled || false; } + /** + * 获取环境兼容性信息 + * + * @returns 环境兼容性信息 + */ + public static getEnvironmentInfo(): EnvironmentInfo | null { + return this._instance?._environmentInfo || null; + } + + /** + * 检查BigInt是否支持 + * + * @returns 是否支持BigInt + */ + public static get supportsBigInt(): boolean { + return this._instance?._environmentInfo.supportsBigInt || false; + } + /** * 场景切换回调 * @@ -408,6 +440,24 @@ export class Core { // 核心系统初始化 } + /** + * 记录兼容性信息 + * + * 在控制台输出当前环境的兼容性信息和建议。 + */ + private logCompatibilityInfo(): void { + const info = this._environmentInfo; + + console.log('ECS Framework 兼容性检测结果:'); + console.log(` 环境: ${info.environment}`); + console.log(` JavaScript引擎: ${info.jsEngine}`); + console.log(` BigInt支持: ${info.supportsBigInt ? '支持' : '不支持'}`); + + if (!info.supportsBigInt) { + console.warn('BigInt兼容模式已启用'); + } + } + /** * 内部更新方法 * diff --git a/src/ECS/Core/BitMaskOptimizer.ts b/src/ECS/Core/BitMaskOptimizer.ts index 07645b59..c7465375 100644 --- a/src/ECS/Core/BitMaskOptimizer.ts +++ b/src/ECS/Core/BitMaskOptimizer.ts @@ -1,9 +1,13 @@ +import { IBigIntLike, BigIntFactory } from '../Utils/BigIntCompatibility'; + /** * 位掩码优化器,用于预计算和缓存常用的组件掩码 + * + * 使用BigInt兼容层确保在所有平台上的正常运行。 */ export class BitMaskOptimizer { private static instance: BitMaskOptimizer; - private maskCache = new Map(); + private maskCache = new Map(); private componentTypeMap = new Map(); private nextComponentId = 0; @@ -35,8 +39,10 @@ export class BitMaskOptimizer { /** * 创建单个组件的掩码 + * @param componentName 组件名称 + * @returns 组件掩码 */ - createSingleComponentMask(componentName: string): bigint { + createSingleComponentMask(componentName: string): IBigIntLike { const cacheKey = `single:${componentName}`; if (this.maskCache.has(cacheKey)) { @@ -48,15 +54,17 @@ export class BitMaskOptimizer { throw new Error(`Component type not registered: ${componentName}`); } - const mask = 1n << BigInt(componentId); + const mask = BigIntFactory.one().shiftLeft(componentId); this.maskCache.set(cacheKey, mask); return mask; } /** * 创建多个组件的组合掩码 + * @param componentNames 组件名称数组 + * @returns 组合掩码 */ - createCombinedMask(componentNames: string[]): bigint { + createCombinedMask(componentNames: string[]): IBigIntLike { const sortedNames = [...componentNames].sort(); const cacheKey = `combined:${sortedNames.join(',')}`; @@ -64,13 +72,14 @@ export class BitMaskOptimizer { return this.maskCache.get(cacheKey)!; } - let mask = 0n; + let mask = BigIntFactory.zero(); for (const componentName of componentNames) { const componentId = this.getComponentTypeId(componentName); if (componentId === undefined) { throw new Error(`Component type not registered: ${componentName}`); } - mask |= 1n << BigInt(componentId); + const componentMask = BigIntFactory.one().shiftLeft(componentId); + mask = mask.or(componentMask); } this.maskCache.set(cacheKey, mask); @@ -79,42 +88,59 @@ export class BitMaskOptimizer { /** * 检查掩码是否包含指定组件 + * @param mask 要检查的掩码 + * @param componentName 组件名称 + * @returns 是否包含指定组件 */ - maskContainsComponent(mask: bigint, componentName: string): boolean { + maskContainsComponent(mask: IBigIntLike, componentName: string): boolean { const componentMask = this.createSingleComponentMask(componentName); - return (mask & componentMask) !== 0n; + return !mask.and(componentMask).isZero(); } /** * 检查掩码是否包含所有指定组件 + * @param mask 要检查的掩码 + * @param componentNames 组件名称数组 + * @returns 是否包含所有指定组件 */ - maskContainsAllComponents(mask: bigint, componentNames: string[]): boolean { + maskContainsAllComponents(mask: IBigIntLike, componentNames: string[]): boolean { const requiredMask = this.createCombinedMask(componentNames); - return (mask & requiredMask) === requiredMask; + const intersection = mask.and(requiredMask); + return intersection.equals(requiredMask); } /** * 检查掩码是否包含任一指定组件 + * @param mask 要检查的掩码 + * @param componentNames 组件名称数组 + * @returns 是否包含任一指定组件 */ - maskContainsAnyComponent(mask: bigint, componentNames: string[]): boolean { + maskContainsAnyComponent(mask: IBigIntLike, componentNames: string[]): boolean { const anyMask = this.createCombinedMask(componentNames); - return (mask & anyMask) !== 0n; + return !mask.and(anyMask).isZero(); } /** * 添加组件到掩码 + * @param mask 原始掩码 + * @param componentName 要添加的组件名称 + * @returns 新的掩码 */ - addComponentToMask(mask: bigint, componentName: string): bigint { + addComponentToMask(mask: IBigIntLike, componentName: string): IBigIntLike { const componentMask = this.createSingleComponentMask(componentName); - return mask | componentMask; + return mask.or(componentMask); } /** * 从掩码中移除组件 + * @param mask 原始掩码 + * @param componentName 要移除的组件名称 + * @returns 新的掩码 */ - removeComponentFromMask(mask: bigint, componentName: string): bigint { + removeComponentFromMask(mask: IBigIntLike, componentName: string): IBigIntLike { const componentMask = this.createSingleComponentMask(componentName); - return mask & ~componentMask; + const notComponentMask = componentMask.not(); + return mask.and(notComponentMask); } /** @@ -155,12 +181,12 @@ export class BitMaskOptimizer { /** * 将掩码转换为组件名称数组 */ - maskToComponentNames(mask: bigint): string[] { + maskToComponentNames(mask: IBigIntLike): string[] { const componentNames: string[] = []; for (const [componentName, componentId] of this.componentTypeMap) { - const componentMask = 1n << BigInt(componentId); - if ((mask & componentMask) !== 0n) { + const componentMask = BigIntFactory.one().shiftLeft(componentId); + if (!mask.and(componentMask).isZero()) { componentNames.push(componentName); } } @@ -171,15 +197,16 @@ export class BitMaskOptimizer { /** * 获取掩码中组件的数量 */ - getComponentCount(mask: bigint): number { + getComponentCount(mask: IBigIntLike): number { let count = 0; - let tempMask = mask; + let tempMask = mask.clone(); + const one = BigIntFactory.one(); - while (tempMask !== 0n) { - if ((tempMask & 1n) !== 0n) { + while (!tempMask.isZero()) { + if (!tempMask.and(one).isZero()) { count++; } - tempMask >>= 1n; + tempMask = tempMask.shiftRight(1); } return count; diff --git a/src/ECS/Core/ComponentStorage.ts b/src/ECS/Core/ComponentStorage.ts index 62edd013..edac40b9 100644 --- a/src/ECS/Core/ComponentStorage.ts +++ b/src/ECS/Core/ComponentStorage.ts @@ -1,4 +1,5 @@ import { Component } from '../Component'; +import { IBigIntLike, BigIntFactory } from '../Utils/BigIntCompatibility'; /** * 组件类型定义 @@ -38,12 +39,12 @@ export class ComponentRegistry { * @param componentType 组件类型 * @returns 位掩码 */ - public static getBitMask(componentType: ComponentType): bigint { + public static getBitMask(componentType: ComponentType): IBigIntLike { const bitIndex = this.componentTypes.get(componentType); if (bitIndex === undefined) { throw new Error(`Component type ${componentType.name} is not registered`); } - return BigInt(1) << BigInt(bitIndex); + return BigIntFactory.one().shiftLeft(bitIndex); } /** @@ -365,12 +366,13 @@ export class ComponentStorageManager { * @param entityId 实体ID * @returns 组件位掩码 */ - public getComponentMask(entityId: number): bigint { - let mask = BigInt(0); + public getComponentMask(entityId: number): IBigIntLike { + let mask = BigIntFactory.zero(); for (const [componentType, storage] of this.storages.entries()) { if (storage.hasComponent(entityId)) { - mask |= ComponentRegistry.getBitMask(componentType as ComponentType); + const componentMask = ComponentRegistry.getBitMask(componentType as ComponentType); + mask = mask.or(componentMask); } } diff --git a/src/ECS/Core/QuerySystem.ts b/src/ECS/Core/QuerySystem.ts index d029abc0..8c7c02ba 100644 --- a/src/ECS/Core/QuerySystem.ts +++ b/src/ECS/Core/QuerySystem.ts @@ -1,6 +1,7 @@ import { Entity } from '../Entity'; import { Component } from '../Component'; import { ComponentRegistry, ComponentType } from './ComponentStorage'; +import { IBigIntLike, BigIntFactory } from '../Utils/BigIntCompatibility'; import { ComponentPoolManager } from './ComponentPool'; import { BitMaskOptimizer } from './BitMaskOptimizer'; @@ -27,7 +28,7 @@ export enum QueryConditionType { export interface QueryCondition { type: QueryConditionType; componentTypes: ComponentType[]; - mask: bigint; + mask: IBigIntLike; } /** @@ -46,7 +47,7 @@ export interface QueryResult { * 实体索引结构 */ interface EntityIndex { - byMask: Map>; + byMask: Map>; byComponentType: Map>; byTag: Map>; byName: Map>; @@ -275,10 +276,11 @@ export class QuerySystem { const mask = entity.componentMask; // 组件掩码索引 - 优化Map操作 - let maskSet = this.entityIndex.byMask.get(mask); + const maskKey = mask.toString(); + let maskSet = this.entityIndex.byMask.get(maskKey); if (!maskSet) { maskSet = new Set(); - this.entityIndex.byMask.set(mask, maskSet); + this.entityIndex.byMask.set(maskKey, maskSet); } maskSet.add(entity); @@ -324,11 +326,12 @@ export class QuerySystem { const mask = entity.componentMask; // 从组件掩码索引移除 - const maskSet = this.entityIndex.byMask.get(mask); + const maskKey = mask.toString(); + const maskSet = this.entityIndex.byMask.get(maskKey); if (maskSet) { maskSet.delete(entity); if (maskSet.size === 0) { - this.entityIndex.byMask.delete(mask); + this.entityIndex.byMask.delete(maskKey); } } @@ -500,7 +503,7 @@ export class QuerySystem { const result: Entity[] = []; for (const entity of smallestSet) { - if ((entity.componentMask & mask) === mask) { + if (entity.componentMask.and(mask).equals(mask)) { result.push(entity); } } @@ -520,7 +523,7 @@ export class QuerySystem { private queryByLinearScan(componentTypes: ComponentType[]): Entity[] { const mask = this.createComponentMask(componentTypes); return this.entities.filter(entity => - (entity.componentMask & mask) === mask + entity.componentMask.and(mask).equals(mask) ); } @@ -617,7 +620,7 @@ export class QuerySystem { const mask = this.createComponentMask(componentTypes); const entities = this.entities.filter(entity => - (entity.componentMask & mask) === BigInt(0) + entity.componentMask.and(mask).isZero() ); this.addToCache(cacheKey, entities); @@ -902,14 +905,14 @@ export class QuerySystem { * @param componentTypes 组件类型列表 * @returns 生成的位掩码 */ - private createComponentMask(componentTypes: ComponentType[]): bigint { - let mask = BigInt(0); + private createComponentMask(componentTypes: ComponentType[]): IBigIntLike { + let mask = BigIntFactory.zero(); let hasValidComponents = false; for (const type of componentTypes) { try { const bitMask = ComponentRegistry.getBitMask(type); - mask |= bitMask; + mask = mask.or(bitMask); hasValidComponents = true; } catch (error) { console.warn(`组件类型 ${type.name} 未注册,跳过`); @@ -918,7 +921,7 @@ export class QuerySystem { // 如果没有有效的组件类型,返回一个不可能匹配的掩码 if (!hasValidComponents) { - return BigInt(-1); // 所有位都是1,不可能与任何实体匹配 + return BigIntFactory.create(-1); // 所有位都是1,不可能与任何实体匹配 } return mask; @@ -1161,12 +1164,12 @@ export class QueryBuilder { /** * 创建组件掩码 */ - private createComponentMask(componentTypes: ComponentType[]): bigint { - let mask = BigInt(0); + private createComponentMask(componentTypes: ComponentType[]): IBigIntLike { + let mask = BigIntFactory.zero(); for (const type of componentTypes) { try { const bitMask = ComponentRegistry.getBitMask(type); - mask |= bitMask; + mask = mask.or(bitMask); } catch (error) { console.warn(`组件类型 ${type.name} 未注册,跳过`); } diff --git a/src/ECS/Entity.ts b/src/ECS/Entity.ts index a12ed01d..de431bcd 100644 --- a/src/ECS/Entity.ts +++ b/src/ECS/Entity.ts @@ -1,6 +1,7 @@ import { Component } from './Component'; import { ComponentRegistry, ComponentType } from './Core/ComponentStorage'; import { EventBus } from './Core/EventBus'; +import { IBigIntLike, BigIntFactory } from './Utils/BigIntCompatibility'; /** * 实体比较器 @@ -152,7 +153,7 @@ export class Entity { * * 用于快速查询实体拥有的组件类型。 */ - private _componentMask: bigint = BigInt(0); + private _componentMask: IBigIntLike = BigIntFactory.zero(); /** * 组件类型到索引的映射 @@ -303,7 +304,7 @@ export class Entity { * * @returns 实体的组件位掩码 */ - public get componentMask(): bigint { + public get componentMask(): IBigIntLike { return this._componentMask; } @@ -345,7 +346,8 @@ export class Entity { this._componentTypeToIndex.set(componentType, index); // 更新位掩码 - this._componentMask |= ComponentRegistry.getBitMask(componentType); + const componentMask = ComponentRegistry.getBitMask(componentType); + this._componentMask = this._componentMask.or(componentMask); return component; } @@ -412,7 +414,7 @@ export class Entity { } const mask = ComponentRegistry.getBitMask(type); - if ((this._componentMask & mask) === BigInt(0)) { + if (this._componentMask.and(mask).isZero()) { return null; } @@ -475,7 +477,7 @@ export class Entity { } const mask = ComponentRegistry.getBitMask(type); - return (this._componentMask & mask) !== BigInt(0); + return !this._componentMask.and(mask).isZero(); } /** @@ -515,7 +517,8 @@ export class Entity { // 更新位掩码 if (ComponentRegistry.isRegistered(componentType)) { - this._componentMask &= ~ComponentRegistry.getBitMask(componentType); + const componentMask = ComponentRegistry.getBitMask(componentType); + this._componentMask = this._componentMask.and(componentMask.not()); } // 从组件存储管理器中移除 @@ -574,7 +577,7 @@ export class Entity { // 清空索引和位掩码 this._componentTypeToIndex.clear(); - this._componentMask = BigInt(0); + this._componentMask = BigIntFactory.zero(); // 移除组件 for (const component of componentsToRemove) { diff --git a/src/ECS/Utils/BigIntCompatibility.ts b/src/ECS/Utils/BigIntCompatibility.ts new file mode 100644 index 00000000..1a926fd6 --- /dev/null +++ b/src/ECS/Utils/BigIntCompatibility.ts @@ -0,0 +1,738 @@ +/** + * BigInt兼容性抽象层 + * + * 为不支持BigInt的环境提供兼容实现,确保ECS框架在所有平台上都能正常运行。 + * 自动检测运行时环境的BigInt支持情况,并提供统一的接口。 + * + * @example + * ```typescript + * // 创建兼容的BigInt值 + * const value = BigIntFactory.create(123); + * + * // 位运算 + * const result = value.or(BigIntFactory.create(456)); + * + * // 检查兼容性 + * console.log(BigIntFactory.isNativeSupported()); // true/false + * ``` + */ + +/** + * BigInt兼容接口 + * + * 定义了BigInt的基本操作接口,支持原生BigInt和兼容实现的统一调用。 + */ +export interface IBigIntLike { + /** + * 获取数值表示 + * @returns 数值 + */ + valueOf(): number; + + /** + * 转换为字符串 + * @param radix 进制,支持2、10、16 + * @returns 字符串表示 + */ + toString(radix?: number): string; + + /** + * 位运算:与 + * @param other 另一个BigInt值 + * @returns 运算结果 + */ + and(other: IBigIntLike): IBigIntLike; + + /** + * 位运算:或 + * @param other 另一个BigInt值 + * @returns 运算结果 + */ + or(other: IBigIntLike): IBigIntLike; + + /** + * 位运算:异或 + * @param other 另一个BigInt值 + * @returns 运算结果 + */ + xor(other: IBigIntLike): IBigIntLike; + + /** + * 位运算:非 + * @param maxBits 最大位数限制 + * @returns 运算结果 + */ + not(maxBits?: number): IBigIntLike; + + /** + * 左移位运算 + * @param bits 移位数 + * @returns 运算结果 + */ + shiftLeft(bits: number): IBigIntLike; + + /** + * 右移位运算 + * @param bits 移位数 + * @returns 运算结果 + */ + shiftRight(bits: number): IBigIntLike; + + /** + * 相等比较 + * @param other 另一个BigInt值 + * @returns 是否相等 + */ + equals(other: IBigIntLike): boolean; + + /** + * 检查是否为零 + * @returns 是否为零 + */ + isZero(): boolean; + + /** + * 创建副本 + * @returns 新的实例 + */ + clone(): IBigIntLike; +} + +/** + * 原生BigInt包装器 + * + * 为支持BigInt的环境提供统一接口包装。 + */ +class NativeBigInt implements IBigIntLike { + constructor(private value: bigint) {} + + valueOf(): number { + return Number(this.value); + } + + toString(radix?: number): string { + if (radix !== undefined && radix !== 10 && radix !== 16 && radix !== 2) { + throw new Error('Only radix 2, 10, and 16 are supported'); + } + const result = this.value.toString(radix); + if (radix === 16) { + return result.toUpperCase(); + } + return result; + } + + and(other: IBigIntLike): IBigIntLike { + const otherBigInt = other instanceof NativeBigInt ? other.value : BigInt(other.valueOf()); + return new NativeBigInt(this.value & otherBigInt); + } + + or(other: IBigIntLike): IBigIntLike { + const otherBigInt = other instanceof NativeBigInt ? other.value : BigInt(other.valueOf()); + return new NativeBigInt(this.value | otherBigInt); + } + + xor(other: IBigIntLike): IBigIntLike { + const otherBigInt = other instanceof NativeBigInt ? other.value : BigInt(other.valueOf()); + return new NativeBigInt(this.value ^ otherBigInt); + } + + not(maxBits: number = 64): IBigIntLike { + const mask = (BigInt(1) << BigInt(maxBits)) - BigInt(1); + return new NativeBigInt((~this.value) & mask); + } + + shiftLeft(bits: number): IBigIntLike { + return new NativeBigInt(this.value << BigInt(bits)); + } + + shiftRight(bits: number): IBigIntLike { + return new NativeBigInt(this.value >> BigInt(bits)); + } + + equals(other: IBigIntLike): boolean { + const otherBigInt = other instanceof NativeBigInt ? other.value : BigInt(other.valueOf()); + return this.value === otherBigInt; + } + + isZero(): boolean { + return this.value === BigInt(0); + } + + clone(): IBigIntLike { + return new NativeBigInt(this.value); + } +} + +/** + * 数组模拟BigInt实现 + * + * 为不支持BigInt的环境提供兼容实现,使用32位数组模拟大整数运算。 + * 性能略低于原生BigInt,但保证功能一致性。 + */ +class ArrayBigInt implements IBigIntLike { + private chunks: number[] = []; // 32位块数组 + private static readonly CHUNK_SIZE = 32; + private static readonly CHUNK_MASK = 0xFFFFFFFF; + private static readonly CHUNK_MAX = 0x100000000; // 2^32 + + /** + * 构造函数 + * @param value 初始值,可以是数值、字符串或数组 + */ + constructor(value: number | string | number[] = 0) { + if (typeof value === 'number') { + this.fromNumber(value); + } else if (typeof value === 'string') { + this.fromString(value); + } else { + this.chunks = value.slice(); + } + this.normalize(); + } + + /** + * 从数值初始化 + * @param value 数值 + */ + private fromNumber(value: number): void { + this.chunks = []; + + // 处理负数(在位运算中通常不会遇到) + if (value < 0) { + value = Math.abs(value); + } + + if (value === 0) { + this.chunks = [0]; + return; + } + + while (value > 0) { + this.chunks.push(value & ArrayBigInt.CHUNK_MASK); + value = Math.floor(value / ArrayBigInt.CHUNK_MAX); + } + } + + /** + * 从字符串初始化 + * @param value 字符串(支持十进制、十六进制、二进制) + */ + private fromString(value: string): void { + value = value.trim(); + + if (value.startsWith('0x') || value.startsWith('0X')) { + // 十六进制 + this.fromHexString(value.substring(2)); + } else if (value.startsWith('0b') || value.startsWith('0B')) { + // 二进制 + this.fromBinaryString(value.substring(2)); + } else { + // 十进制 + this.fromDecimalString(value); + } + } + + /** + * 从十六进制字符串初始化 + * @param hex 十六进制字符串 + */ + private fromHexString(hex: string): void { + this.chunks = [0]; + + for (let i = hex.length - 1; i >= 0; i -= 8) { + const start = Math.max(0, i - 7); + const chunk = parseInt(hex.substring(start, i + 1), 16); + this.chunks.push(chunk); + } + + this.normalize(); + } + + /** + * 从二进制字符串初始化 + * @param binary 二进制字符串 + */ + private fromBinaryString(binary: string): void { + this.chunks = [0]; + + for (let i = binary.length - 1; i >= 0; i -= 32) { + const start = Math.max(0, i - 31); + const chunk = parseInt(binary.substring(start, i + 1), 2); + this.chunks.push(chunk); + } + + this.normalize(); + } + + /** + * 从十进制字符串初始化 + * @param decimal 十进制字符串 + */ + private fromDecimalString(decimal: string): void { + // 简化实现,直接转换为数值(在ECS位运算场景中通常是小数值) + const num = parseInt(decimal, 10); + this.fromNumber(num); + } + + /** + * 规范化数组,移除前导零 + */ + private normalize(): void { + while (this.chunks.length > 1 && this.chunks[this.chunks.length - 1] === 0) { + this.chunks.pop(); + } + + if (this.chunks.length === 0) { + this.chunks = [0]; + } + } + + valueOf(): number { + let result = 0; + let multiplier = 1; + + for (const chunk of this.chunks) { + result += chunk * multiplier; + multiplier *= ArrayBigInt.CHUNK_MAX; + + // 防止溢出 + if (multiplier > Number.MAX_SAFE_INTEGER) { + break; + } + } + + return result; + } + + toString(radix: number = 10): string { + if (radix !== 10 && radix !== 16 && radix !== 2) { + throw new Error('Only radix 2, 10, and 16 are supported'); + } + + if (this.isZero()) { + return '0'; + } + + if (radix === 10) { + // 简化实现,转换为数值 + return this.valueOf().toString(10); + } else if (radix === 16) { + let result = ''; + for (let i = this.chunks.length - 1; i >= 0; i--) { + const hex = this.chunks[i].toString(16); + result += i === this.chunks.length - 1 ? hex : hex.padStart(8, '0'); + } + return result.toUpperCase(); + } else if (radix === 2) { + let result = ''; + for (let i = this.chunks.length - 1; i >= 0; i--) { + const binary = this.chunks[i].toString(2); + result += i === this.chunks.length - 1 ? binary : binary.padStart(32, '0'); + } + return result; + } + + return this.valueOf().toString(radix); + } + + and(other: IBigIntLike): IBigIntLike { + const otherArray = other as ArrayBigInt; + const maxLength = Math.max(this.chunks.length, otherArray.chunks.length); + const result: number[] = []; + + for (let i = 0; i < maxLength; i++) { + const a = i < this.chunks.length ? this.chunks[i] : 0; + const b = i < otherArray.chunks.length ? otherArray.chunks[i] : 0; + result.push(a & b); + } + + return new ArrayBigInt(result); + } + + or(other: IBigIntLike): IBigIntLike { + const otherArray = other as ArrayBigInt; + const maxLength = Math.max(this.chunks.length, otherArray.chunks.length); + const result: number[] = []; + + for (let i = 0; i < maxLength; i++) { + const a = i < this.chunks.length ? this.chunks[i] : 0; + const b = i < otherArray.chunks.length ? otherArray.chunks[i] : 0; + result.push(a | b); + } + + return new ArrayBigInt(result); + } + + xor(other: IBigIntLike): IBigIntLike { + const otherArray = other as ArrayBigInt; + const maxLength = Math.max(this.chunks.length, otherArray.chunks.length); + const result: number[] = []; + + for (let i = 0; i < maxLength; i++) { + const a = i < this.chunks.length ? this.chunks[i] : 0; + const b = i < otherArray.chunks.length ? otherArray.chunks[i] : 0; + result.push(a ^ b); + } + + return new ArrayBigInt(result); + } + + not(maxBits: number = 64): IBigIntLike { + const maxChunks = Math.ceil(maxBits / ArrayBigInt.CHUNK_SIZE); + const result: number[] = []; + + for (let i = 0; i < maxChunks; i++) { + const chunk = i < this.chunks.length ? this.chunks[i] : 0; + + if (i === maxChunks - 1) { + // 最后一个块需要处理剩余位数 + const remainingBits = maxBits % ArrayBigInt.CHUNK_SIZE; + if (remainingBits > 0) { + const mask = (1 << remainingBits) - 1; + result.push((~chunk) & mask); + } else { + result.push((~chunk) & ArrayBigInt.CHUNK_MASK); + } + } else { + result.push((~chunk) & ArrayBigInt.CHUNK_MASK); + } + } + + return new ArrayBigInt(result); + } + + shiftLeft(bits: number): IBigIntLike { + if (bits === 0) { + return this.clone(); + } + + if (bits < 0) { + return this.shiftRight(-bits); + } + + const chunkShift = Math.floor(bits / ArrayBigInt.CHUNK_SIZE); + const bitShift = bits % ArrayBigInt.CHUNK_SIZE; + + const result: number[] = new Array(chunkShift).fill(0); + + if (bitShift === 0) { + // 整块移位 + result.push(...this.chunks); + } else { + // 部分位移位 + let carry = 0; + for (const chunk of this.chunks) { + const shifted = (chunk << bitShift) | carry; + result.push(shifted & ArrayBigInt.CHUNK_MASK); + carry = chunk >>> (ArrayBigInt.CHUNK_SIZE - bitShift); + } + + if (carry > 0) { + result.push(carry); + } + } + + return new ArrayBigInt(result); + } + + shiftRight(bits: number): IBigIntLike { + if (bits === 0) { + return this.clone(); + } + + if (bits < 0) { + return this.shiftLeft(-bits); + } + + const chunkShift = Math.floor(bits / ArrayBigInt.CHUNK_SIZE); + const bitShift = bits % ArrayBigInt.CHUNK_SIZE; + + if (chunkShift >= this.chunks.length) { + return new ArrayBigInt(0); + } + + const result: number[] = []; + + if (bitShift === 0) { + // 整块移位 + for (let i = chunkShift; i < this.chunks.length; i++) { + result.push(this.chunks[i]); + } + } else { + // 部分位移位 + let carry = 0; + for (let i = this.chunks.length - 1; i >= chunkShift; i--) { + const chunk = this.chunks[i]; + const shifted = (carry << (ArrayBigInt.CHUNK_SIZE - bitShift)) | (chunk >>> bitShift); + result.unshift(shifted); + carry = chunk & ((1 << bitShift) - 1); + } + } + + return new ArrayBigInt(result.length > 0 ? result : [0]); + } + + equals(other: IBigIntLike): boolean { + if (!(other instanceof ArrayBigInt)) { + return false; + } + + if (this.chunks.length !== other.chunks.length) { + return false; + } + + for (let i = 0; i < this.chunks.length; i++) { + if (this.chunks[i] !== other.chunks[i]) { + return false; + } + } + + return true; + } + + isZero(): boolean { + return this.chunks.length === 1 && this.chunks[0] === 0; + } + + clone(): IBigIntLike { + return new ArrayBigInt(this.chunks.slice()); + } +} + +/** + * BigInt工厂类 + * + * 自动检测运行时环境的BigInt支持情况,并提供统一的创建接口。 + * 在支持BigInt的环境中使用原生实现,在不支持的环境中使用兼容实现。 + */ +export class BigIntFactory { + private static _supportsBigInt: boolean | null = null; + // 缓存检测结果以避免重复检测 + + /** + * 检查是否支持原生BigInt + * @returns 是否支持原生BigInt + */ + public static isNativeSupported(): boolean { + if (this._supportsBigInt === null) { + this._supportsBigInt = this.detectBigIntSupport(); + } + return this._supportsBigInt; + } + + /** + * 检测BigInt支持情况 + * @returns 是否支持BigInt + */ + private static detectBigIntSupport(): boolean { + try { + // 检查BigInt构造函数是否存在 + if (typeof BigInt === 'undefined') { + return false; + } + + // 检查基本BigInt操作 + const test1 = BigInt(1); + const test2 = BigInt(2); + const result = test1 | test2; + + // 检查字面量支持 + const literal = eval('1n'); // 使用eval避免语法错误 + + // 检查类型 + if (typeof result !== 'bigint' || typeof literal !== 'bigint') { + return false; + } + + // 检查基本运算 + const shifted = test1 << BigInt(1); + const compared = test1 === BigInt(1); + + return typeof shifted === 'bigint' && compared === true; + } catch (error) { + // 任何异常都表示不支持 + return false; + } + } + + /** + * 创建BigInt兼容值 + * @param value 初始值 + * @returns IBigIntLike实例 + */ + public static create(value: number | string | bigint = 0): IBigIntLike { + if (this.isNativeSupported()) { + let bigintValue: bigint; + + if (typeof value === 'bigint') { + bigintValue = value; + } else if (typeof value === 'string') { + bigintValue = BigInt(value); + } else { + bigintValue = BigInt(value); + } + + return new NativeBigInt(bigintValue); + } else { + // 转换bigint类型到兼容类型 + let compatValue: number | string; + + if (typeof value === 'bigint') { + compatValue = value.toString(); + } else { + compatValue = value; + } + + return new ArrayBigInt(compatValue); + } + } + + /** + * 创建零值 + * @returns 零值的IBigIntLike实例 + */ + public static zero(): IBigIntLike { + return this.create(0); + } + + /** + * 创建1值 + * @returns 1值的IBigIntLike实例 + */ + public static one(): IBigIntLike { + return this.create(1); + } + + /** + * 从二进制字符串创建 + * @param binary 二进制字符串 + * @returns IBigIntLike实例 + */ + public static fromBinaryString(binary: string): IBigIntLike { + if (this.isNativeSupported()) { + const value = BigInt('0b' + binary); + return new NativeBigInt(value); + } else { + return new ArrayBigInt('0b' + binary); + } + } + + /** + * 从十六进制字符串创建 + * @param hex 十六进制字符串 + * @returns IBigIntLike实例 + */ + public static fromHexString(hex: string): IBigIntLike { + if (this.isNativeSupported()) { + const cleanHex = hex.replace(/^0x/i, ''); + const value = BigInt('0x' + cleanHex); + return new NativeBigInt(value); + } else { + return new ArrayBigInt(hex); + } + } + + /** + * 获取环境信息 + * @returns 环境信息对象 + */ + public static getEnvironmentInfo(): EnvironmentInfo { + return { + supportsBigInt: this.isNativeSupported(), + environment: this.detectEnvironment(), + jsEngine: this.detectJSEngine() + }; + } + + /** + * 检测运行环境 + * @returns 环境类型 + */ + private static detectEnvironment(): string { + if (typeof window !== 'undefined') { + // 浏览器环境 + if (typeof navigator !== 'undefined') { + const userAgent = navigator.userAgent; + + if (userAgent.includes('Chrome')) { + const match = userAgent.match(/Chrome\/(\d+)/); + const version = match ? parseInt(match[1]) : 0; + return `Chrome ${version}`; + } + + if (userAgent.includes('Firefox')) { + const match = userAgent.match(/Firefox\/(\d+)/); + const version = match ? parseInt(match[1]) : 0; + return `Firefox ${version}`; + } + + if (userAgent.includes('Safari') && !userAgent.includes('Chrome')) { + const match = userAgent.match(/Version\/(\d+)/); + const version = match ? parseInt(match[1]) : 0; + return `Safari ${version}`; + } + + return 'Browser (Unknown)'; + } + + return 'Browser'; + } else if (typeof global !== 'undefined') { + // Node.js环境 + if (typeof process !== 'undefined' && process.version) { + return `Node.js ${process.version}`; + } + return 'Node.js'; + } else if (typeof (globalThis as any).wx !== 'undefined') { + // 微信小程序 + return 'WeChat MiniProgram'; + } else if (typeof (globalThis as any).cc !== 'undefined') { + // Cocos Creator + return 'Cocos Creator'; + } else if (typeof (globalThis as any).Laya !== 'undefined') { + // Laya引擎 + return 'Laya Engine'; + } + + return 'Unknown'; + } + + /** + * 检测JavaScript引擎 + * @returns JS引擎信息 + */ + private static detectJSEngine(): string { + try { + // V8引擎特征检测 + if (typeof process !== 'undefined' && process.versions && process.versions.v8) { + return `V8 ${process.versions.v8}`; + } + + // SpiderMonkey特征检测 + if (typeof (globalThis as any).Components !== 'undefined') { + return 'SpiderMonkey'; + } + + // JavaScriptCore特征检测 + if (typeof window !== 'undefined' && typeof (window as any).safari !== 'undefined') { + return 'JavaScriptCore'; + } + + return 'Unknown'; + } catch { + return 'Unknown'; + } + } + +} + +/** + * 环境信息接口 + */ +export interface EnvironmentInfo { + /** 是否支持BigInt */ + supportsBigInt: boolean; + /** 运行环境 */ + environment: string; + /** JavaScript引擎 */ + jsEngine: string; +} \ No newline at end of file diff --git a/src/ECS/Utils/Bits.ts b/src/ECS/Utils/Bits.ts index 8c490cee..834039a8 100644 --- a/src/ECS/Utils/Bits.ts +++ b/src/ECS/Utils/Bits.ts @@ -1,60 +1,97 @@ +import { IBigIntLike, BigIntFactory } from './BigIntCompatibility'; + /** * 高性能位操作类 - * 基于BigInt实现,支持任意数量的位操作 + * + * 基于BigInt实现,支持任意数量的位操作。 + * 自动适配运行环境,在不支持BigInt的环境中使用兼容实现。 + * + * @example + * ```typescript + * const bits = new Bits(); + * bits.set(0); + * bits.set(5); + * console.log(bits.get(0)); // true + * console.log(bits.get(1)); // false + * ``` */ export class Bits { - private _value: bigint = 0n; + private _value: IBigIntLike; - constructor(initialValue: bigint = 0n) { - this._value = initialValue; + /** + * 构造函数 + * @param initialValue 初始值,可以是IBigIntLike或数值 + */ + constructor(initialValue?: IBigIntLike | number | string) { + if (initialValue && typeof initialValue === 'object') { + this._value = initialValue; + } else { + this._value = BigIntFactory.create(initialValue || 0); + } } /** * 设置指定位置的位为1 + * @param index 位索引(从0开始) + * @throws {Error} 当索引为负数时抛出错误 */ public set(index: number): void { if (index < 0) { throw new Error('Bit index cannot be negative'); } - this._value |= (1n << BigInt(index)); + const mask = BigIntFactory.one().shiftLeft(index); + this._value = this._value.or(mask); } /** * 清除指定位置的位(设为0) + * @param index 位索引(从0开始) + * @throws {Error} 当索引为负数时抛出错误 */ public clear(index: number): void { if (index < 0) { throw new Error('Bit index cannot be negative'); } - this._value &= ~(1n << BigInt(index)); + const mask = BigIntFactory.one().shiftLeft(index).not(); + this._value = this._value.and(mask); } /** * 获取指定位置的位值 + * @param index 位索引(从0开始) + * @returns 位值(true表示1,false表示0) */ public get(index: number): boolean { if (index < 0) { return false; } - return (this._value & (1n << BigInt(index))) !== 0n; + const mask = BigIntFactory.one().shiftLeft(index); + return !this._value.and(mask).isZero(); } /** * 检查是否包含所有指定的位 + * @param other 另一个Bits对象 + * @returns 是否包含所有指定的位 */ public containsAll(other: Bits): boolean { - return (this._value & other._value) === other._value; + const intersection = this._value.and(other._value); + return intersection.equals(other._value); } /** * 检查是否包含任意一个指定的位 + * @param other 另一个Bits对象 + * @returns 是否包含任意一个指定的位 */ public intersects(other: Bits): boolean { - return (this._value & other._value) !== 0n; + return !this._value.and(other._value).isZero(); } /** * 检查是否不包含任何指定的位 + * @param other 另一个Bits对象 + * @returns 是否不包含任何指定的位 */ public excludes(other: Bits): boolean { return !this.intersects(other); @@ -64,28 +101,31 @@ export class Bits { * 清空所有位 */ public clearAll(): void { - this._value = 0n; + this._value = BigIntFactory.zero(); } /** * 检查是否为空(没有设置任何位) + * @returns 是否为空 */ public isEmpty(): boolean { - return this._value === 0n; + return this._value.isZero(); } /** * 获取设置的位数量 + * @returns 设置为1的位数量 */ public cardinality(): number { let count = 0; - let value = this._value; + let value = this._value.clone(); - while (value > 0n) { - if (value & 1n) { + while (!value.isZero()) { + const one = BigIntFactory.one(); + if (!value.and(one).isZero()) { count++; } - value >>= 1n; + value = value.shiftRight(1); } return count; @@ -93,74 +133,91 @@ export class Bits { /** * 位运算:与 + * @param other 另一个Bits对象 + * @returns 新的Bits对象,包含与运算结果 */ public and(other: Bits): Bits { - return new Bits(this._value & other._value); + return new Bits(this._value.and(other._value)); } /** * 位运算:或 + * @param other 另一个Bits对象 + * @returns 新的Bits对象,包含或运算结果 */ public or(other: Bits): Bits { - return new Bits(this._value | other._value); + return new Bits(this._value.or(other._value)); } /** * 位运算:异或 + * @param other 另一个Bits对象 + * @returns 新的Bits对象,包含异或运算结果 */ public xor(other: Bits): Bits { - return new Bits(this._value ^ other._value); + return new Bits(this._value.xor(other._value)); } /** * 位运算:非 + * @param maxBits 最大位数限制,默认64位 + * @returns 新的Bits对象,包含非运算结果 */ public not(maxBits: number = 64): Bits { - const mask = (1n << BigInt(maxBits)) - 1n; - return new Bits((~this._value) & mask); + return new Bits(this._value.not(maxBits)); } /** * 复制另一个Bits对象 + * @param other 要复制的Bits对象 */ public copyFrom(other: Bits): void { - this._value = other._value; + this._value = other._value.clone(); } /** * 创建当前Bits的副本 + * @returns 新的Bits对象副本 */ public clone(): Bits { - return new Bits(this._value); + return new Bits(this._value.clone()); } /** - * 获取原始BigInt值 + * 获取原始值 + * @returns 原始的IBigIntLike值 */ - public getValue(): bigint { + public getValue(): IBigIntLike { return this._value; } /** - * 设置原始BigInt值 + * 设置原始值 + * @param value 新的值,可以是IBigIntLike或数值 */ - public setValue(value: bigint): void { - this._value = value; + public setValue(value: IBigIntLike | number | string): void { + if (typeof value === 'object') { + this._value = value; + } else { + this._value = BigIntFactory.create(value); + } } /** * 获取调试信息 + * @returns 返回显示设置位索引的字符串 */ public toString(): string { const bits: string[] = []; let index = 0; - let value = this._value; + let value = this._value.clone(); - while (value > 0n) { - if (value & 1n) { + while (!value.isZero()) { + const one = BigIntFactory.one(); + if (!value.and(one).isZero()) { bits.push(index.toString()); } - value >>= 1n; + value = value.shiftRight(1); index++; } @@ -169,6 +226,8 @@ export class Bits { /** * 获取二进制表示 + * @param maxBits 最大位数,默认64位 + * @returns 二进制字符串表示 */ public toBinaryString(maxBits: number = 64): string { let result = ''; @@ -183,6 +242,7 @@ export class Bits { /** * 获取十六进制表示 + * @returns 十六进制字符串表示 */ public toHexString(): string { return '0x' + this._value.toString(16).toUpperCase(); @@ -190,42 +250,48 @@ export class Bits { /** * 从二进制字符串创建Bits + * @param binaryString 二进制字符串 + * @returns 新的Bits对象 */ public static fromBinaryString(binaryString: string): Bits { const cleanString = binaryString.replace(/\s/g, ''); - const value = BigInt('0b' + cleanString); + const value = BigIntFactory.fromBinaryString(cleanString); return new Bits(value); } /** * 从十六进制字符串创建Bits + * @param hexString 十六进制字符串 + * @returns 新的Bits对象 */ public static fromHexString(hexString: string): Bits { - const cleanString = hexString.replace(/^0x/i, ''); - const value = BigInt('0x' + cleanString); + const value = BigIntFactory.fromHexString(hexString); return new Bits(value); } /** * 比较两个Bits对象是否相等 + * @param other 另一个Bits对象 + * @returns 是否相等 */ public equals(other: Bits): boolean { - return this._value === other._value; + return this._value.equals(other._value); } /** * 获取最高位的索引 + * @returns 最高位的索引,如果为空则返回-1 */ public getHighestBitIndex(): number { - if (this._value === 0n) { + if (this._value.isZero()) { return -1; } let index = 0; - let value = this._value; + let value = this._value.clone(); - while (value > 1n) { - value >>= 1n; + while (!value.shiftRight(1).isZero()) { + value = value.shiftRight(1); index++; } @@ -234,17 +300,19 @@ export class Bits { /** * 获取最低位的索引 + * @returns 最低位的索引,如果为空则返回-1 */ public getLowestBitIndex(): number { - if (this._value === 0n) { + if (this._value.isZero()) { return -1; } let index = 0; - let value = this._value; + let value = this._value.clone(); + const one = BigIntFactory.one(); - while ((value & 1n) === 0n) { - value >>= 1n; + while (value.and(one).isZero()) { + value = value.shiftRight(1); index++; } diff --git a/tests/ECS/Core/BitMaskOptimizer.test.ts b/tests/ECS/Core/BitMaskOptimizer.test.ts index afdea410..08ae141d 100644 --- a/tests/ECS/Core/BitMaskOptimizer.test.ts +++ b/tests/ECS/Core/BitMaskOptimizer.test.ts @@ -1,4 +1,5 @@ import { BitMaskOptimizer } from '../../../src/ECS/Core/BitMaskOptimizer'; +import { BigIntFactory } from '../../../src/ECS/Utils/BigIntCompatibility'; describe('BitMaskOptimizer - 位掩码优化器测试', () => { let optimizer: BitMaskOptimizer; @@ -58,15 +59,15 @@ describe('BitMaskOptimizer - 位掩码优化器测试', () => { it('应该能够创建单个组件的掩码', () => { const mask = optimizer.createSingleComponentMask('Transform'); - expect(mask).toBe(1n); + expect(mask.toString()).toBe('1'); }); it('不同组件应该有不同的掩码', () => { const transformMask = optimizer.createSingleComponentMask('Transform'); const velocityMask = optimizer.createSingleComponentMask('Velocity'); - expect(transformMask).toBe(1n); - expect(velocityMask).toBe(2n); + expect(transformMask.toString()).toBe('1'); + expect(velocityMask.toString()).toBe('2'); }); it('创建未注册组件的掩码应该抛出错误', () => { @@ -91,7 +92,7 @@ describe('BitMaskOptimizer - 位掩码优化器测试', () => { it('应该能够创建多个组件的组合掩码', () => { const mask = optimizer.createCombinedMask(['Transform', 'Velocity']); - expect(mask).toBe(3n); // 1n | 2n = 3n + expect(mask.toString()).toBe('3'); // 1 | 2 = 3 }); it('组件顺序不应该影响掩码结果', () => { @@ -102,12 +103,12 @@ describe('BitMaskOptimizer - 位掩码优化器测试', () => { it('三个组件的组合掩码应该正确', () => { const mask = optimizer.createCombinedMask(['Transform', 'Velocity', 'Health']); - expect(mask).toBe(7n); // 1n | 2n | 4n = 7n + expect(mask.toString()).toBe('7'); // 1 | 2 | 4 = 7 }); it('空数组应该返回0掩码', () => { const mask = optimizer.createCombinedMask([]); - expect(mask).toBe(0n); + expect(mask.isZero()).toBe(true); }); it('包含未注册组件应该抛出错误', () => { @@ -181,7 +182,7 @@ describe('BitMaskOptimizer - 位掩码优化器测试', () => { const originalMask = optimizer.createSingleComponentMask('Transform'); const newMask = optimizer.removeComponentFromMask(originalMask, 'Velocity'); - expect(newMask).toBe(originalMask); + expect(newMask.equals(originalMask)).toBe(true); }); }); @@ -203,7 +204,7 @@ describe('BitMaskOptimizer - 位掩码优化器测试', () => { }); it('空掩码应该返回空数组', () => { - const componentNames = optimizer.maskToComponentNames(0n); + const componentNames = optimizer.maskToComponentNames(BigIntFactory.zero()); expect(componentNames).toEqual([]); }); @@ -218,7 +219,7 @@ describe('BitMaskOptimizer - 位掩码优化器测试', () => { }); it('空掩码的组件数量应该为0', () => { - expect(optimizer.getComponentCount(0n)).toBe(0); + expect(optimizer.getComponentCount(BigIntFactory.zero())).toBe(0); }); }); @@ -288,12 +289,12 @@ describe('BitMaskOptimizer - 位掩码优化器测试', () => { } const mask = optimizer.createSingleComponentMask('Component63'); - expect(mask).toBe(1n << 63n); + expect(mask.toString()).toBe(BigIntFactory.one().shiftLeft(63).toString()); }); it('空组件名称数组的组合掩码', () => { const mask = optimizer.createCombinedMask([]); - expect(mask).toBe(0n); + expect(mask.isZero()).toBe(true); expect(optimizer.getComponentCount(mask)).toBe(0); }); }); diff --git a/tests/ECS/Core/ComponentStorage.test.ts b/tests/ECS/Core/ComponentStorage.test.ts index 36dc1092..05f6fb3a 100644 --- a/tests/ECS/Core/ComponentStorage.test.ts +++ b/tests/ECS/Core/ComponentStorage.test.ts @@ -5,6 +5,7 @@ import { ComponentType } from '../../../src/ECS/Core/ComponentStorage'; import { Component } from '../../../src/ECS/Component'; +import { BigIntFactory } from '../../../src/ECS/Utils/BigIntCompatibility'; // 测试组件类 class TestComponent extends Component { @@ -93,8 +94,8 @@ describe('ComponentRegistry - 组件注册表测试', () => { const mask1 = ComponentRegistry.getBitMask(TestComponent); const mask2 = ComponentRegistry.getBitMask(PositionComponent); - expect(mask1).toBe(BigInt(1)); // 2^0 - expect(mask2).toBe(BigInt(2)); // 2^1 + expect(mask1.toString()).toBe('1'); // 2^0 + expect(mask2.toString()).toBe('2'); // 2^1 }); test('应该能够获取组件的位索引', () => { @@ -491,13 +492,12 @@ describe('ComponentStorageManager - 组件存储管理器测试', () => { const mask = manager.getComponentMask(1); // 应该包含TestComponent(位0)和PositionComponent(位1)的掩码 - const expectedMask = BigInt(1) | BigInt(2); // 0b11 - expect(mask).toBe(expectedMask); + expect(mask.toString()).toBe('3'); // 1 | 2 = 3 }); test('没有组件的实体应该有零掩码', () => { const mask = manager.getComponentMask(999); - expect(mask).toBe(BigInt(0)); + expect(mask.isZero()).toBe(true); }); test('添加和移除组件应该更新掩码', () => { @@ -506,15 +506,15 @@ describe('ComponentStorageManager - 组件存储管理器测试', () => { manager.addComponent(1, new TestComponent(100)); let mask = manager.getComponentMask(1); - expect(mask).toBe(BigInt(1)); + expect(mask.toString()).toBe('1'); manager.addComponent(1, new PositionComponent(10, 20)); mask = manager.getComponentMask(1); - expect(mask).toBe(BigInt(3)); // 0b11 + expect(mask.toString()).toBe('3'); // 0b11 manager.removeComponent(1, TestComponent); mask = manager.getComponentMask(1); - expect(mask).toBe(BigInt(2)); // 0b10 + expect(mask.toString()).toBe('2'); // 0b10 }); }); diff --git a/tests/ECS/Utils/BigIntCompatibility.test.ts b/tests/ECS/Utils/BigIntCompatibility.test.ts new file mode 100644 index 00000000..e48b19f9 --- /dev/null +++ b/tests/ECS/Utils/BigIntCompatibility.test.ts @@ -0,0 +1,338 @@ +import { + BigIntFactory, + IBigIntLike, + EnvironmentInfo +} from '../../../src/ECS/Utils/BigIntCompatibility'; + +describe('BigInt兼容性测试', () => { + describe('BigIntFactory环境检测', () => { + it('应该能够检测BigInt支持情况', () => { + const isSupported = BigIntFactory.isNativeSupported(); + expect(typeof isSupported).toBe('boolean'); + }); + + it('应该返回环境信息', () => { + const envInfo = BigIntFactory.getEnvironmentInfo(); + expect(envInfo).toBeDefined(); + expect(typeof envInfo.supportsBigInt).toBe('boolean'); + expect(typeof envInfo.environment).toBe('string'); + expect(typeof envInfo.jsEngine).toBe('string'); + }); + + it('环境信息应该包含合理的字段', () => { + const envInfo = BigIntFactory.getEnvironmentInfo(); + expect(envInfo.environment).not.toBe(''); + expect(envInfo.jsEngine).not.toBe(''); + }); + }); + + describe('BigIntFactory基本创建', () => { + it('应该能够创建零值', () => { + const zero = BigIntFactory.zero(); + expect(zero.isZero()).toBe(true); + expect(zero.toString()).toBe('0'); + }); + + it('应该能够创建1值', () => { + const one = BigIntFactory.one(); + expect(one.isZero()).toBe(false); + expect(one.toString()).toBe('1'); + }); + + it('应该能够从数值创建', () => { + const value = BigIntFactory.create(42); + expect(value.toString()).toBe('42'); + expect(value.valueOf()).toBe(42); + }); + + it('应该能够从字符串创建', () => { + const value = BigIntFactory.create('123'); + expect(value.toString()).toBe('123'); + }); + + it('应该能够从原生BigInt创建(如果支持)', () => { + if (BigIntFactory.isNativeSupported()) { + const value = BigIntFactory.create(BigInt(456)); + expect(value.toString()).toBe('456'); + } + }); + }); + + describe('IBigIntLike基本操作', () => { + let value1: IBigIntLike; + let value2: IBigIntLike; + + beforeEach(() => { + value1 = BigIntFactory.create(5); // 101 in binary + value2 = BigIntFactory.create(3); // 011 in binary + }); + + it('should支持字符串转换', () => { + expect(value1.toString()).toBe('5'); + expect(value2.toString()).toBe('3'); + }); + + it('应该支持十六进制转换', () => { + const value = BigIntFactory.create(255); + expect(value.toString(16)).toBe('FF'); + }); + + it('应该支持二进制转换', () => { + expect(value1.toString(2)).toBe('101'); + expect(value2.toString(2)).toBe('11'); + }); + + it('应该支持相等比较', () => { + const value1Copy = BigIntFactory.create(5); + expect(value1.equals(value1Copy)).toBe(true); + expect(value1.equals(value2)).toBe(false); + }); + + it('应该支持零值检查', () => { + const zero = BigIntFactory.zero(); + expect(zero.isZero()).toBe(true); + expect(value1.isZero()).toBe(false); + }); + + it('应该支持克隆操作', () => { + const cloned = value1.clone(); + expect(cloned.equals(value1)).toBe(true); + expect(cloned).not.toBe(value1); // 不同的对象引用 + }); + }); + + describe('位运算操作', () => { + let value1: IBigIntLike; // 5 = 101 + let value2: IBigIntLike; // 3 = 011 + + beforeEach(() => { + value1 = BigIntFactory.create(5); + value2 = BigIntFactory.create(3); + }); + + it('AND运算应该正确', () => { + const result = value1.and(value2); + expect(result.toString()).toBe('1'); // 101 & 011 = 001 + }); + + it('OR运算应该正确', () => { + const result = value1.or(value2); + expect(result.toString()).toBe('7'); // 101 | 011 = 111 + }); + + it('XOR运算应该正确', () => { + const result = value1.xor(value2); + expect(result.toString()).toBe('6'); // 101 ^ 011 = 110 + }); + + it('NOT运算应该正确(8位限制)', () => { + const value = BigIntFactory.create(5); // 00000101 + const result = value.not(8); + expect(result.toString()).toBe('250'); // 11111010 = 250 + }); + + it('左移位运算应该正确', () => { + const result = value1.shiftLeft(2); + expect(result.toString()).toBe('20'); // 101 << 2 = 10100 = 20 + }); + + it('右移位运算应该正确', () => { + const result = value1.shiftRight(1); + expect(result.toString()).toBe('2'); // 101 >> 1 = 10 = 2 + }); + + it('移位0位应该返回相同值', () => { + const result = value1.shiftLeft(0); + expect(result.equals(value1)).toBe(true); + }); + + it('右移超过位数应该返回0', () => { + const result = value1.shiftRight(10); + expect(result.isZero()).toBe(true); + }); + }); + + describe('复杂位运算场景', () => { + it('应该正确处理大数值位运算', () => { + const large1 = BigIntFactory.create(0xFFFFFFFF); // 32位全1 + const large2 = BigIntFactory.create(0x12345678); + + const andResult = large1.and(large2); + expect(andResult.toString(16)).toBe('12345678'); + + const orResult = large1.or(large2); + expect(orResult.toString(16)).toBe('FFFFFFFF'); + }); + + it('应该正确处理连续位运算', () => { + let result = BigIntFactory.create(1); + + // 构建 111111 (6个1) + for (let i = 1; i < 6; i++) { + const shifted = BigIntFactory.one().shiftLeft(i); + result = result.or(shifted); + } + + expect(result.toString()).toBe('63'); // 111111 = 63 + expect(result.toString(2)).toBe('111111'); + }); + + it('应该正确处理掩码操作', () => { + const value = BigIntFactory.create(0b10110101); // 181 + const mask = BigIntFactory.create(0b00001111); // 15, 低4位掩码 + + const masked = value.and(mask); + expect(masked.toString()).toBe('5'); // 0101 = 5 + }); + }); + + describe('字符串解析功能', () => { + it('应该支持从二进制字符串创建', () => { + const value = BigIntFactory.fromBinaryString('10101'); + expect(value.toString()).toBe('21'); + }); + + it('应该支持从十六进制字符串创建', () => { + const value1 = BigIntFactory.fromHexString('0xFF'); + const value2 = BigIntFactory.fromHexString('FF'); + + expect(value1.toString()).toBe('255'); + expect(value2.toString()).toBe('255'); + expect(value1.equals(value2)).toBe(true); + }); + + it('应该正确处理大的十六进制值', () => { + const value = BigIntFactory.fromHexString('0x12345678'); + expect(value.toString()).toBe('305419896'); + }); + + it('应该正确处理长二进制字符串', () => { + const binaryStr = '11111111111111111111111111111111'; // 32个1 + const value = BigIntFactory.fromBinaryString(binaryStr); + expect(value.toString()).toBe('4294967295'); // 2^32 - 1 + }); + }); + + describe('边界情况和错误处理', () => { + it('应该正确处理零值的所有操作', () => { + const zero = BigIntFactory.zero(); + const one = BigIntFactory.one(); + + expect(zero.and(one).isZero()).toBe(true); + expect(zero.or(one).equals(one)).toBe(true); + expect(zero.xor(one).equals(one)).toBe(true); + expect(zero.shiftLeft(5).isZero()).toBe(true); + expect(zero.shiftRight(5).isZero()).toBe(true); + }); + + it('应该正确处理1值的位运算', () => { + const one = BigIntFactory.one(); + const zero = BigIntFactory.zero(); + + expect(one.and(zero).isZero()).toBe(true); + expect(one.or(zero).equals(one)).toBe(true); + expect(one.xor(zero).equals(one)).toBe(true); + }); + + it('应该处理不支持的字符串进制', () => { + const value = BigIntFactory.create(255); + expect(() => value.toString(8)).toThrow(); + }); + + it('NOT运算应该正确处理不同的位数限制', () => { + const value = BigIntFactory.one(); // 1 + + const not8 = value.not(8); + expect(not8.toString()).toBe('254'); // 11111110 = 254 + + const not16 = value.not(16); + expect(not16.toString()).toBe('65534'); // 1111111111111110 = 65534 + }); + }); + + describe('性能和兼容性测试', () => { + it('两种实现应该产生相同的运算结果', () => { + // 测试各种运算在两种模式下的一致性 + const testCases = [ + { a: 0, b: 0 }, + { a: 1, b: 1 }, + { a: 5, b: 3 }, + { a: 255, b: 128 }, + { a: 65535, b: 32768 } + ]; + + testCases.forEach(({ a, b }) => { + const val1 = BigIntFactory.create(a); + const val2 = BigIntFactory.create(b); + + // 基本运算 + const and = val1.and(val2); + const or = val1.or(val2); + const xor = val1.xor(val2); + + // 验证运算结果的一致性 + expect(and.toString()).toBe((a & b).toString()); + expect(or.toString()).toBe((a | b).toString()); + expect(xor.toString()).toBe((a ^ b).toString()); + }); + }); + + it('大量运算应该保持高性能', () => { + const startTime = performance.now(); + + let result = BigIntFactory.zero(); + for (let i = 0; i < 1000; i++) { + const value = BigIntFactory.create(i); + result = result.or(value.shiftLeft(i % 32)); + } + + const endTime = performance.now(); + expect(endTime - startTime).toBeLessThan(1000); // 应该在1秒内完成 + expect(result.isZero()).toBe(false); + }); + + it('应该支持ECS框架中常见的位掩码操作', () => { + // 模拟组件位掩码 + const componentMasks: IBigIntLike[] = []; + + // 创建64个组件的位掩码 + for (let i = 0; i < 64; i++) { + componentMasks.push(BigIntFactory.one().shiftLeft(i)); + } + + // 组合多个组件掩码 + let combinedMask = BigIntFactory.zero(); + for (let i = 0; i < 10; i++) { + combinedMask = combinedMask.or(componentMasks[i * 2]); + } + + // 检查是否包含特定组件 + for (let i = 0; i < 10; i++) { + const hasComponent = !combinedMask.and(componentMasks[i * 2]).isZero(); + expect(hasComponent).toBe(true); + + const hasOtherComponent = !combinedMask.and(componentMasks[i * 2 + 1]).isZero(); + expect(hasOtherComponent).toBe(false); + } + }); + }); + + describe('与Core类集成测试', () => { + // 这里我们不直接导入Core来避免循环依赖,而是测试工厂方法 + it('BigIntFactory应该能够为Core提供环境信息', () => { + const envInfo = BigIntFactory.getEnvironmentInfo(); + + // 验证所有必需的字段都存在 + expect(envInfo.supportsBigInt).toBeDefined(); + expect(envInfo.environment).toBeDefined(); + expect(envInfo.jsEngine).toBeDefined(); + }); + + it('应该提供详细的环境检测信息', () => { + const envInfo = BigIntFactory.getEnvironmentInfo(); + + // 环境信息应该有意义 + expect(envInfo.environment).not.toBe('Unknown'); + }); + }); +}); \ No newline at end of file diff --git a/tests/ECS/Utils/Bits.test.ts b/tests/ECS/Utils/Bits.test.ts index eb21dfc4..b0c18d1b 100644 --- a/tests/ECS/Utils/Bits.test.ts +++ b/tests/ECS/Utils/Bits.test.ts @@ -1,4 +1,5 @@ import { Bits } from '../../../src/ECS/Utils/Bits'; +import { BigIntFactory } from '../../../src/ECS/Utils/BigIntCompatibility'; describe('Bits - 高性能位操作类测试', () => { let bits: Bits; @@ -11,12 +12,12 @@ describe('Bits - 高性能位操作类测试', () => { it('应该能够创建空的Bits对象', () => { expect(bits).toBeDefined(); expect(bits.isEmpty()).toBe(true); - expect(bits.getValue()).toBe(0n); + expect(bits.getValue().isZero()).toBe(true); }); it('应该能够使用初始值创建Bits对象', () => { - const bitsWithValue = new Bits(5n); // 二进制: 101 - expect(bitsWithValue.getValue()).toBe(5n); + const bitsWithValue = new Bits(BigIntFactory.create(5)); // 二进制: 101 + expect(bitsWithValue.getValue().toString()).toBe('5'); expect(bitsWithValue.isEmpty()).toBe(false); expect(bitsWithValue.get(0)).toBe(true); // 第0位 expect(bitsWithValue.get(1)).toBe(false); // 第1位 @@ -25,7 +26,7 @@ describe('Bits - 高性能位操作类测试', () => { it('默认构造函数应该创建值为0的对象', () => { const defaultBits = new Bits(); - expect(defaultBits.getValue()).toBe(0n); + expect(defaultBits.getValue().isZero()).toBe(true); }); }); @@ -33,22 +34,22 @@ describe('Bits - 高性能位操作类测试', () => { it('应该能够设置指定位置的位', () => { bits.set(0); expect(bits.get(0)).toBe(true); - expect(bits.getValue()).toBe(1n); + expect(bits.getValue().toString()).toBe('1'); bits.set(3); expect(bits.get(3)).toBe(true); - expect(bits.getValue()).toBe(9n); // 1001 in binary + expect(bits.getValue().toString()).toBe('9'); // 1001 in binary }); it('应该能够清除指定位置的位', () => { bits.set(0); bits.set(1); bits.set(2); - expect(bits.getValue()).toBe(7n); // 111 in binary + expect(bits.getValue().toString()).toBe('7'); // 111 in binary bits.clear(1); expect(bits.get(1)).toBe(false); - expect(bits.getValue()).toBe(5n); // 101 in binary + expect(bits.getValue().toString()).toBe('5'); // 101 in binary }); it('重复设置同一位应该保持不变', () => { @@ -56,12 +57,12 @@ describe('Bits - 高性能位操作类测试', () => { const value1 = bits.getValue(); bits.set(0); const value2 = bits.getValue(); - expect(value1).toBe(value2); + expect(value1.equals(value2)).toBe(true); }); it('清除未设置的位应该安全', () => { bits.clear(5); - expect(bits.getValue()).toBe(0n); + expect(bits.getValue().isZero()).toBe(true); }); it('设置负索引应该抛出错误', () => { @@ -122,7 +123,7 @@ describe('Bits - 高性能位操作类测试', () => { it('AND运算应该正确', () => { const result = bits.and(otherBits); - expect(result.getValue()).toBe(4n); // 10101 & 01110 = 00100 = 4 + expect(result.getValue().toString()).toBe('4'); // 10101 & 01110 = 00100 = 4 expect(result.get(2)).toBe(true); expect(result.get(0)).toBe(false); expect(result.get(1)).toBe(false); @@ -130,7 +131,7 @@ describe('Bits - 高性能位操作类测试', () => { it('OR运算应该正确', () => { const result = bits.or(otherBits); - expect(result.getValue()).toBe(31n); // 10101 | 01110 = 11111 = 31 + expect(result.getValue().toString()).toBe('31'); // 10101 | 01110 = 11111 = 31 expect(result.get(0)).toBe(true); expect(result.get(1)).toBe(true); expect(result.get(2)).toBe(true); @@ -140,7 +141,7 @@ describe('Bits - 高性能位操作类测试', () => { it('XOR运算应该正确', () => { const result = bits.xor(otherBits); - expect(result.getValue()).toBe(27n); // 10101 ^ 01110 = 11011 = 27 + expect(result.getValue().toString()).toBe('27'); // 10101 ^ 01110 = 11011 = 27 expect(result.get(0)).toBe(true); expect(result.get(1)).toBe(true); expect(result.get(2)).toBe(false); // 相同位XOR为0 @@ -149,16 +150,16 @@ describe('Bits - 高性能位操作类测试', () => { }); it('NOT运算应该正确', () => { - const simpleBits = new Bits(5n); // 101 in binary + const simpleBits = new Bits(BigIntFactory.create(5)); // 101 in binary const result = simpleBits.not(8); // 限制为8位 - expect(result.getValue()).toBe(250n); // ~00000101 = 11111010 = 250 (8位) + expect(result.getValue().toString()).toBe('250'); // ~00000101 = 11111010 = 250 (8位) }); it('NOT运算默认64位应该正确', () => { - const simpleBits = new Bits(1n); + const simpleBits = new Bits(BigIntFactory.create(1)); const result = simpleBits.not(); - const expected = (1n << 64n) - 2n; // 64位全1减去最低位 - expect(result.getValue()).toBe(expected); + const expected = BigIntFactory.one().shiftLeft(64).valueOf() - 2; // 64位全1减去最低位 + expect(result.getValue().valueOf()).toBe(expected); }); }); @@ -253,7 +254,7 @@ describe('Bits - 高性能位操作类测试', () => { expect(bits.isEmpty()).toBe(false); bits.clearAll(); expect(bits.isEmpty()).toBe(true); - expect(bits.getValue()).toBe(0n); + expect(bits.getValue().isZero()).toBe(true); }); it('clearAll后应该能重新设置位', () => { @@ -275,14 +276,14 @@ describe('Bits - 高性能位操作类测试', () => { const newBits = new Bits(); newBits.copyFrom(bits); - expect(newBits.getValue()).toBe(bits.getValue()); + expect(newBits.getValue().equals(bits.getValue())).toBe(true); expect(newBits.equals(bits)).toBe(true); }); it('clone应该创建相同的副本', () => { const clonedBits = bits.clone(); - expect(clonedBits.getValue()).toBe(bits.getValue()); + expect(clonedBits.getValue().equals(bits.getValue())).toBe(true); expect(clonedBits.equals(bits)).toBe(true); expect(clonedBits).not.toBe(bits); // 应该是不同的对象 }); @@ -298,12 +299,12 @@ describe('Bits - 高性能位操作类测试', () => { describe('值操作', () => { it('getValue和setValue应该正确工作', () => { - bits.setValue(42n); - expect(bits.getValue()).toBe(42n); + bits.setValue(42); + expect(bits.getValue().toString()).toBe('42'); }); it('setValue应该正确反映在位操作中', () => { - bits.setValue(5n); // 101 in binary + bits.setValue(5); // 101 in binary expect(bits.get(0)).toBe(true); expect(bits.get(1)).toBe(false); expect(bits.get(2)).toBe(true); @@ -312,7 +313,7 @@ describe('Bits - 高性能位操作类测试', () => { it('setValue为0应该清空所有位', () => { bits.set(1); bits.set(2); - bits.setValue(0n); + bits.setValue(0); expect(bits.isEmpty()).toBe(true); }); }); @@ -351,24 +352,24 @@ describe('Bits - 高性能位操作类测试', () => { it('fromBinaryString应该正确解析', () => { const parsedBits = Bits.fromBinaryString('10101'); - expect(parsedBits.getValue()).toBe(21n); + expect(parsedBits.getValue().toString()).toBe('21'); expect(parsedBits.equals(bits)).toBe(true); }); it('fromBinaryString应该处理带空格的字符串', () => { const parsedBits = Bits.fromBinaryString('0001 0101'); - expect(parsedBits.getValue()).toBe(21n); + expect(parsedBits.getValue().toString()).toBe('21'); }); it('fromHexString应该正确解析', () => { const parsedBits = Bits.fromHexString('0x15'); - expect(parsedBits.getValue()).toBe(21n); + expect(parsedBits.getValue().toString()).toBe('21'); expect(parsedBits.equals(bits)).toBe(true); }); it('fromHexString应该处理不带0x前缀的字符串', () => { const parsedBits = Bits.fromHexString('15'); - expect(parsedBits.getValue()).toBe(21n); + expect(parsedBits.getValue().toString()).toBe('21'); }); }); @@ -515,7 +516,7 @@ describe('Bits - 高性能位操作类测试', () => { describe('边界情况和错误处理', () => { it('应该处理0值的各种操作', () => { - const zeroBits = new Bits(0n); + const zeroBits = new Bits(BigIntFactory.zero()); expect(zeroBits.isEmpty()).toBe(true); expect(zeroBits.cardinality()).toBe(0); expect(zeroBits.getHighestBitIndex()).toBe(-1); @@ -523,7 +524,7 @@ describe('Bits - 高性能位操作类测试', () => { }); it('应该处理最大BigInt值', () => { - const maxBits = new Bits(BigInt(Number.MAX_SAFE_INTEGER)); + const maxBits = new Bits(BigIntFactory.create(Number.MAX_SAFE_INTEGER)); expect(maxBits.isEmpty()).toBe(false); expect(maxBits.cardinality()).toBeGreaterThan(0); });