对bigint进行兼容处理(不支持的环境回退到兼容模式)
This commit is contained in:
50
src/Core.ts
50
src/Core.ts
@@ -8,6 +8,7 @@ import { ECSFluentAPI, createECSAPI } from './ECS/Core/FluentAPI';
|
|||||||
import { Scene } from './ECS/Scene';
|
import { Scene } from './ECS/Scene';
|
||||||
import { DebugManager } from './Utils/Debug';
|
import { DebugManager } from './Utils/Debug';
|
||||||
import { ICoreConfig, IECSDebugConfig } from './Types';
|
import { ICoreConfig, IECSDebugConfig } from './Types';
|
||||||
|
import { BigIntFactory, EnvironmentInfo } from './ECS/Utils/BigIntCompatibility';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 游戏引擎核心类
|
* 游戏引擎核心类
|
||||||
@@ -121,6 +122,11 @@ export class Core {
|
|||||||
*/
|
*/
|
||||||
private _config: ICoreConfig;
|
private _config: ICoreConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 兼容性信息
|
||||||
|
*/
|
||||||
|
private _environmentInfo: EnvironmentInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建核心实例
|
* 创建核心实例
|
||||||
*
|
*
|
||||||
@@ -136,6 +142,9 @@ export class Core {
|
|||||||
...config
|
...config
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 检测环境兼容性
|
||||||
|
this._environmentInfo = BigIntFactory.getEnvironmentInfo();
|
||||||
|
|
||||||
// 初始化管理器
|
// 初始化管理器
|
||||||
this._timerManager = new TimerManager();
|
this._timerManager = new TimerManager();
|
||||||
Core.registerGlobalManager(this._timerManager);
|
Core.registerGlobalManager(this._timerManager);
|
||||||
@@ -159,6 +168,11 @@ export class Core {
|
|||||||
this._debugManager = new DebugManager(this, this._config.debugConfig);
|
this._debugManager = new DebugManager(this, this._config.debugConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 在调试模式下显示兼容性信息
|
||||||
|
if (this._config.debug) {
|
||||||
|
this.logCompatibilityInfo();
|
||||||
|
}
|
||||||
|
|
||||||
this.initialize();
|
this.initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,6 +393,24 @@ export class Core {
|
|||||||
return this._instance?._config.debugConfig?.enabled || false;
|
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兼容模式已启用');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 内部更新方法
|
* 内部更新方法
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
|
import { IBigIntLike, BigIntFactory } from '../Utils/BigIntCompatibility';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 位掩码优化器,用于预计算和缓存常用的组件掩码
|
* 位掩码优化器,用于预计算和缓存常用的组件掩码
|
||||||
|
*
|
||||||
|
* 使用BigInt兼容层确保在所有平台上的正常运行。
|
||||||
*/
|
*/
|
||||||
export class BitMaskOptimizer {
|
export class BitMaskOptimizer {
|
||||||
private static instance: BitMaskOptimizer;
|
private static instance: BitMaskOptimizer;
|
||||||
private maskCache = new Map<string, bigint>();
|
private maskCache = new Map<string, IBigIntLike>();
|
||||||
private componentTypeMap = new Map<string, number>();
|
private componentTypeMap = new Map<string, number>();
|
||||||
private nextComponentId = 0;
|
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}`;
|
const cacheKey = `single:${componentName}`;
|
||||||
|
|
||||||
if (this.maskCache.has(cacheKey)) {
|
if (this.maskCache.has(cacheKey)) {
|
||||||
@@ -48,15 +54,17 @@ export class BitMaskOptimizer {
|
|||||||
throw new Error(`Component type not registered: ${componentName}`);
|
throw new Error(`Component type not registered: ${componentName}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const mask = 1n << BigInt(componentId);
|
const mask = BigIntFactory.one().shiftLeft(componentId);
|
||||||
this.maskCache.set(cacheKey, mask);
|
this.maskCache.set(cacheKey, mask);
|
||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建多个组件的组合掩码
|
* 创建多个组件的组合掩码
|
||||||
|
* @param componentNames 组件名称数组
|
||||||
|
* @returns 组合掩码
|
||||||
*/
|
*/
|
||||||
createCombinedMask(componentNames: string[]): bigint {
|
createCombinedMask(componentNames: string[]): IBigIntLike {
|
||||||
const sortedNames = [...componentNames].sort();
|
const sortedNames = [...componentNames].sort();
|
||||||
const cacheKey = `combined:${sortedNames.join(',')}`;
|
const cacheKey = `combined:${sortedNames.join(',')}`;
|
||||||
|
|
||||||
@@ -64,13 +72,14 @@ export class BitMaskOptimizer {
|
|||||||
return this.maskCache.get(cacheKey)!;
|
return this.maskCache.get(cacheKey)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mask = 0n;
|
let mask = BigIntFactory.zero();
|
||||||
for (const componentName of componentNames) {
|
for (const componentName of componentNames) {
|
||||||
const componentId = this.getComponentTypeId(componentName);
|
const componentId = this.getComponentTypeId(componentName);
|
||||||
if (componentId === undefined) {
|
if (componentId === undefined) {
|
||||||
throw new Error(`Component type not registered: ${componentName}`);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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[] = [];
|
const componentNames: string[] = [];
|
||||||
|
|
||||||
for (const [componentName, componentId] of this.componentTypeMap) {
|
for (const [componentName, componentId] of this.componentTypeMap) {
|
||||||
const componentMask = 1n << BigInt(componentId);
|
const componentMask = BigIntFactory.one().shiftLeft(componentId);
|
||||||
if ((mask & componentMask) !== 0n) {
|
if (!mask.and(componentMask).isZero()) {
|
||||||
componentNames.push(componentName);
|
componentNames.push(componentName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,15 +197,16 @@ export class BitMaskOptimizer {
|
|||||||
/**
|
/**
|
||||||
* 获取掩码中组件的数量
|
* 获取掩码中组件的数量
|
||||||
*/
|
*/
|
||||||
getComponentCount(mask: bigint): number {
|
getComponentCount(mask: IBigIntLike): number {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
let tempMask = mask;
|
let tempMask = mask.clone();
|
||||||
|
const one = BigIntFactory.one();
|
||||||
|
|
||||||
while (tempMask !== 0n) {
|
while (!tempMask.isZero()) {
|
||||||
if ((tempMask & 1n) !== 0n) {
|
if (!tempMask.and(one).isZero()) {
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
tempMask >>= 1n;
|
tempMask = tempMask.shiftRight(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Component } from '../Component';
|
import { Component } from '../Component';
|
||||||
|
import { IBigIntLike, BigIntFactory } from '../Utils/BigIntCompatibility';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 组件类型定义
|
* 组件类型定义
|
||||||
@@ -38,12 +39,12 @@ export class ComponentRegistry {
|
|||||||
* @param componentType 组件类型
|
* @param componentType 组件类型
|
||||||
* @returns 位掩码
|
* @returns 位掩码
|
||||||
*/
|
*/
|
||||||
public static getBitMask<T extends Component>(componentType: ComponentType<T>): bigint {
|
public static getBitMask<T extends Component>(componentType: ComponentType<T>): IBigIntLike {
|
||||||
const bitIndex = this.componentTypes.get(componentType);
|
const bitIndex = this.componentTypes.get(componentType);
|
||||||
if (bitIndex === undefined) {
|
if (bitIndex === undefined) {
|
||||||
throw new Error(`Component type ${componentType.name} is not registered`);
|
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
|
* @param entityId 实体ID
|
||||||
* @returns 组件位掩码
|
* @returns 组件位掩码
|
||||||
*/
|
*/
|
||||||
public getComponentMask(entityId: number): bigint {
|
public getComponentMask(entityId: number): IBigIntLike {
|
||||||
let mask = BigInt(0);
|
let mask = BigIntFactory.zero();
|
||||||
|
|
||||||
for (const [componentType, storage] of this.storages.entries()) {
|
for (const [componentType, storage] of this.storages.entries()) {
|
||||||
if (storage.hasComponent(entityId)) {
|
if (storage.hasComponent(entityId)) {
|
||||||
mask |= ComponentRegistry.getBitMask(componentType as ComponentType);
|
const componentMask = ComponentRegistry.getBitMask(componentType as ComponentType);
|
||||||
|
mask = mask.or(componentMask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Entity } from '../Entity';
|
import { Entity } from '../Entity';
|
||||||
import { Component } from '../Component';
|
import { Component } from '../Component';
|
||||||
import { ComponentRegistry, ComponentType } from './ComponentStorage';
|
import { ComponentRegistry, ComponentType } from './ComponentStorage';
|
||||||
|
import { IBigIntLike, BigIntFactory } from '../Utils/BigIntCompatibility';
|
||||||
|
|
||||||
import { ComponentPoolManager } from './ComponentPool';
|
import { ComponentPoolManager } from './ComponentPool';
|
||||||
import { BitMaskOptimizer } from './BitMaskOptimizer';
|
import { BitMaskOptimizer } from './BitMaskOptimizer';
|
||||||
@@ -27,7 +28,7 @@ export enum QueryConditionType {
|
|||||||
export interface QueryCondition {
|
export interface QueryCondition {
|
||||||
type: QueryConditionType;
|
type: QueryConditionType;
|
||||||
componentTypes: ComponentType[];
|
componentTypes: ComponentType[];
|
||||||
mask: bigint;
|
mask: IBigIntLike;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,7 +47,7 @@ export interface QueryResult {
|
|||||||
* 实体索引结构
|
* 实体索引结构
|
||||||
*/
|
*/
|
||||||
interface EntityIndex {
|
interface EntityIndex {
|
||||||
byMask: Map<bigint, Set<Entity>>;
|
byMask: Map<string, Set<Entity>>;
|
||||||
byComponentType: Map<ComponentType, Set<Entity>>;
|
byComponentType: Map<ComponentType, Set<Entity>>;
|
||||||
byTag: Map<number, Set<Entity>>;
|
byTag: Map<number, Set<Entity>>;
|
||||||
byName: Map<string, Set<Entity>>;
|
byName: Map<string, Set<Entity>>;
|
||||||
@@ -275,10 +276,11 @@ export class QuerySystem {
|
|||||||
const mask = entity.componentMask;
|
const mask = entity.componentMask;
|
||||||
|
|
||||||
// 组件掩码索引 - 优化Map操作
|
// 组件掩码索引 - 优化Map操作
|
||||||
let maskSet = this.entityIndex.byMask.get(mask);
|
const maskKey = mask.toString();
|
||||||
|
let maskSet = this.entityIndex.byMask.get(maskKey);
|
||||||
if (!maskSet) {
|
if (!maskSet) {
|
||||||
maskSet = new Set();
|
maskSet = new Set();
|
||||||
this.entityIndex.byMask.set(mask, maskSet);
|
this.entityIndex.byMask.set(maskKey, maskSet);
|
||||||
}
|
}
|
||||||
maskSet.add(entity);
|
maskSet.add(entity);
|
||||||
|
|
||||||
@@ -324,11 +326,12 @@ export class QuerySystem {
|
|||||||
const mask = entity.componentMask;
|
const mask = entity.componentMask;
|
||||||
|
|
||||||
// 从组件掩码索引移除
|
// 从组件掩码索引移除
|
||||||
const maskSet = this.entityIndex.byMask.get(mask);
|
const maskKey = mask.toString();
|
||||||
|
const maskSet = this.entityIndex.byMask.get(maskKey);
|
||||||
if (maskSet) {
|
if (maskSet) {
|
||||||
maskSet.delete(entity);
|
maskSet.delete(entity);
|
||||||
if (maskSet.size === 0) {
|
if (maskSet.size === 0) {
|
||||||
this.entityIndex.byMask.delete(mask);
|
this.entityIndex.byMask.delete(maskKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,7 +503,7 @@ export class QuerySystem {
|
|||||||
const result: Entity[] = [];
|
const result: Entity[] = [];
|
||||||
|
|
||||||
for (const entity of smallestSet) {
|
for (const entity of smallestSet) {
|
||||||
if ((entity.componentMask & mask) === mask) {
|
if (entity.componentMask.and(mask).equals(mask)) {
|
||||||
result.push(entity);
|
result.push(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -520,7 +523,7 @@ export class QuerySystem {
|
|||||||
private queryByLinearScan(componentTypes: ComponentType[]): Entity[] {
|
private queryByLinearScan(componentTypes: ComponentType[]): Entity[] {
|
||||||
const mask = this.createComponentMask(componentTypes);
|
const mask = this.createComponentMask(componentTypes);
|
||||||
return this.entities.filter(entity =>
|
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 mask = this.createComponentMask(componentTypes);
|
||||||
const entities = this.entities.filter(entity =>
|
const entities = this.entities.filter(entity =>
|
||||||
(entity.componentMask & mask) === BigInt(0)
|
entity.componentMask.and(mask).isZero()
|
||||||
);
|
);
|
||||||
|
|
||||||
this.addToCache(cacheKey, entities);
|
this.addToCache(cacheKey, entities);
|
||||||
@@ -902,14 +905,14 @@ export class QuerySystem {
|
|||||||
* @param componentTypes 组件类型列表
|
* @param componentTypes 组件类型列表
|
||||||
* @returns 生成的位掩码
|
* @returns 生成的位掩码
|
||||||
*/
|
*/
|
||||||
private createComponentMask(componentTypes: ComponentType[]): bigint {
|
private createComponentMask(componentTypes: ComponentType[]): IBigIntLike {
|
||||||
let mask = BigInt(0);
|
let mask = BigIntFactory.zero();
|
||||||
let hasValidComponents = false;
|
let hasValidComponents = false;
|
||||||
|
|
||||||
for (const type of componentTypes) {
|
for (const type of componentTypes) {
|
||||||
try {
|
try {
|
||||||
const bitMask = ComponentRegistry.getBitMask(type);
|
const bitMask = ComponentRegistry.getBitMask(type);
|
||||||
mask |= bitMask;
|
mask = mask.or(bitMask);
|
||||||
hasValidComponents = true;
|
hasValidComponents = true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`组件类型 ${type.name} 未注册,跳过`);
|
console.warn(`组件类型 ${type.name} 未注册,跳过`);
|
||||||
@@ -918,7 +921,7 @@ export class QuerySystem {
|
|||||||
|
|
||||||
// 如果没有有效的组件类型,返回一个不可能匹配的掩码
|
// 如果没有有效的组件类型,返回一个不可能匹配的掩码
|
||||||
if (!hasValidComponents) {
|
if (!hasValidComponents) {
|
||||||
return BigInt(-1); // 所有位都是1,不可能与任何实体匹配
|
return BigIntFactory.create(-1); // 所有位都是1,不可能与任何实体匹配
|
||||||
}
|
}
|
||||||
|
|
||||||
return mask;
|
return mask;
|
||||||
@@ -1161,12 +1164,12 @@ export class QueryBuilder {
|
|||||||
/**
|
/**
|
||||||
* 创建组件掩码
|
* 创建组件掩码
|
||||||
*/
|
*/
|
||||||
private createComponentMask(componentTypes: ComponentType[]): bigint {
|
private createComponentMask(componentTypes: ComponentType[]): IBigIntLike {
|
||||||
let mask = BigInt(0);
|
let mask = BigIntFactory.zero();
|
||||||
for (const type of componentTypes) {
|
for (const type of componentTypes) {
|
||||||
try {
|
try {
|
||||||
const bitMask = ComponentRegistry.getBitMask(type);
|
const bitMask = ComponentRegistry.getBitMask(type);
|
||||||
mask |= bitMask;
|
mask = mask.or(bitMask);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`组件类型 ${type.name} 未注册,跳过`);
|
console.warn(`组件类型 ${type.name} 未注册,跳过`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Component } from './Component';
|
import { Component } from './Component';
|
||||||
import { ComponentRegistry, ComponentType } from './Core/ComponentStorage';
|
import { ComponentRegistry, ComponentType } from './Core/ComponentStorage';
|
||||||
import { EventBus } from './Core/EventBus';
|
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 实体的组件位掩码
|
* @returns 实体的组件位掩码
|
||||||
*/
|
*/
|
||||||
public get componentMask(): bigint {
|
public get componentMask(): IBigIntLike {
|
||||||
return this._componentMask;
|
return this._componentMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,7 +346,8 @@ export class Entity {
|
|||||||
this._componentTypeToIndex.set(componentType, index);
|
this._componentTypeToIndex.set(componentType, index);
|
||||||
|
|
||||||
// 更新位掩码
|
// 更新位掩码
|
||||||
this._componentMask |= ComponentRegistry.getBitMask(componentType);
|
const componentMask = ComponentRegistry.getBitMask(componentType);
|
||||||
|
this._componentMask = this._componentMask.or(componentMask);
|
||||||
|
|
||||||
return component;
|
return component;
|
||||||
}
|
}
|
||||||
@@ -412,7 +414,7 @@ export class Entity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const mask = ComponentRegistry.getBitMask(type);
|
const mask = ComponentRegistry.getBitMask(type);
|
||||||
if ((this._componentMask & mask) === BigInt(0)) {
|
if (this._componentMask.and(mask).isZero()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,7 +477,7 @@ export class Entity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const mask = ComponentRegistry.getBitMask(type);
|
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)) {
|
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._componentTypeToIndex.clear();
|
||||||
this._componentMask = BigInt(0);
|
this._componentMask = BigIntFactory.zero();
|
||||||
|
|
||||||
// 移除组件
|
// 移除组件
|
||||||
for (const component of componentsToRemove) {
|
for (const component of componentsToRemove) {
|
||||||
|
|||||||
738
src/ECS/Utils/BigIntCompatibility.ts
Normal file
738
src/ECS/Utils/BigIntCompatibility.ts
Normal file
@@ -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;
|
||||||
|
}
|
||||||
@@ -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 {
|
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
|
* 设置指定位置的位为1
|
||||||
|
* @param index 位索引(从0开始)
|
||||||
|
* @throws {Error} 当索引为负数时抛出错误
|
||||||
*/
|
*/
|
||||||
public set(index: number): void {
|
public set(index: number): void {
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
throw new Error('Bit index cannot be negative');
|
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)
|
* 清除指定位置的位(设为0)
|
||||||
|
* @param index 位索引(从0开始)
|
||||||
|
* @throws {Error} 当索引为负数时抛出错误
|
||||||
*/
|
*/
|
||||||
public clear(index: number): void {
|
public clear(index: number): void {
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
throw new Error('Bit index cannot be negative');
|
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 {
|
public get(index: number): boolean {
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
return false;
|
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 {
|
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 {
|
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 {
|
public excludes(other: Bits): boolean {
|
||||||
return !this.intersects(other);
|
return !this.intersects(other);
|
||||||
@@ -64,28 +101,31 @@ export class Bits {
|
|||||||
* 清空所有位
|
* 清空所有位
|
||||||
*/
|
*/
|
||||||
public clearAll(): void {
|
public clearAll(): void {
|
||||||
this._value = 0n;
|
this._value = BigIntFactory.zero();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查是否为空(没有设置任何位)
|
* 检查是否为空(没有设置任何位)
|
||||||
|
* @returns 是否为空
|
||||||
*/
|
*/
|
||||||
public isEmpty(): boolean {
|
public isEmpty(): boolean {
|
||||||
return this._value === 0n;
|
return this._value.isZero();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取设置的位数量
|
* 获取设置的位数量
|
||||||
|
* @returns 设置为1的位数量
|
||||||
*/
|
*/
|
||||||
public cardinality(): number {
|
public cardinality(): number {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
let value = this._value;
|
let value = this._value.clone();
|
||||||
|
|
||||||
while (value > 0n) {
|
while (!value.isZero()) {
|
||||||
if (value & 1n) {
|
const one = BigIntFactory.one();
|
||||||
|
if (!value.and(one).isZero()) {
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
value >>= 1n;
|
value = value.shiftRight(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
@@ -93,74 +133,91 @@ export class Bits {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 位运算:与
|
* 位运算:与
|
||||||
|
* @param other 另一个Bits对象
|
||||||
|
* @returns 新的Bits对象,包含与运算结果
|
||||||
*/
|
*/
|
||||||
public and(other: Bits): 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 {
|
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 {
|
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 {
|
public not(maxBits: number = 64): Bits {
|
||||||
const mask = (1n << BigInt(maxBits)) - 1n;
|
return new Bits(this._value.not(maxBits));
|
||||||
return new Bits((~this._value) & mask);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 复制另一个Bits对象
|
* 复制另一个Bits对象
|
||||||
|
* @param other 要复制的Bits对象
|
||||||
*/
|
*/
|
||||||
public copyFrom(other: Bits): void {
|
public copyFrom(other: Bits): void {
|
||||||
this._value = other._value;
|
this._value = other._value.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建当前Bits的副本
|
* 创建当前Bits的副本
|
||||||
|
* @returns 新的Bits对象副本
|
||||||
*/
|
*/
|
||||||
public clone(): 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;
|
return this._value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置原始BigInt值
|
* 设置原始值
|
||||||
|
* @param value 新的值,可以是IBigIntLike或数值
|
||||||
*/
|
*/
|
||||||
public setValue(value: bigint): void {
|
public setValue(value: IBigIntLike | number | string): void {
|
||||||
this._value = value;
|
if (typeof value === 'object') {
|
||||||
|
this._value = value;
|
||||||
|
} else {
|
||||||
|
this._value = BigIntFactory.create(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取调试信息
|
* 获取调试信息
|
||||||
|
* @returns 返回显示设置位索引的字符串
|
||||||
*/
|
*/
|
||||||
public toString(): string {
|
public toString(): string {
|
||||||
const bits: string[] = [];
|
const bits: string[] = [];
|
||||||
let index = 0;
|
let index = 0;
|
||||||
let value = this._value;
|
let value = this._value.clone();
|
||||||
|
|
||||||
while (value > 0n) {
|
while (!value.isZero()) {
|
||||||
if (value & 1n) {
|
const one = BigIntFactory.one();
|
||||||
|
if (!value.and(one).isZero()) {
|
||||||
bits.push(index.toString());
|
bits.push(index.toString());
|
||||||
}
|
}
|
||||||
value >>= 1n;
|
value = value.shiftRight(1);
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,6 +226,8 @@ export class Bits {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取二进制表示
|
* 获取二进制表示
|
||||||
|
* @param maxBits 最大位数,默认64位
|
||||||
|
* @returns 二进制字符串表示
|
||||||
*/
|
*/
|
||||||
public toBinaryString(maxBits: number = 64): string {
|
public toBinaryString(maxBits: number = 64): string {
|
||||||
let result = '';
|
let result = '';
|
||||||
@@ -183,6 +242,7 @@ export class Bits {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取十六进制表示
|
* 获取十六进制表示
|
||||||
|
* @returns 十六进制字符串表示
|
||||||
*/
|
*/
|
||||||
public toHexString(): string {
|
public toHexString(): string {
|
||||||
return '0x' + this._value.toString(16).toUpperCase();
|
return '0x' + this._value.toString(16).toUpperCase();
|
||||||
@@ -190,42 +250,48 @@ export class Bits {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 从二进制字符串创建Bits
|
* 从二进制字符串创建Bits
|
||||||
|
* @param binaryString 二进制字符串
|
||||||
|
* @returns 新的Bits对象
|
||||||
*/
|
*/
|
||||||
public static fromBinaryString(binaryString: string): Bits {
|
public static fromBinaryString(binaryString: string): Bits {
|
||||||
const cleanString = binaryString.replace(/\s/g, '');
|
const cleanString = binaryString.replace(/\s/g, '');
|
||||||
const value = BigInt('0b' + cleanString);
|
const value = BigIntFactory.fromBinaryString(cleanString);
|
||||||
return new Bits(value);
|
return new Bits(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从十六进制字符串创建Bits
|
* 从十六进制字符串创建Bits
|
||||||
|
* @param hexString 十六进制字符串
|
||||||
|
* @returns 新的Bits对象
|
||||||
*/
|
*/
|
||||||
public static fromHexString(hexString: string): Bits {
|
public static fromHexString(hexString: string): Bits {
|
||||||
const cleanString = hexString.replace(/^0x/i, '');
|
const value = BigIntFactory.fromHexString(hexString);
|
||||||
const value = BigInt('0x' + cleanString);
|
|
||||||
return new Bits(value);
|
return new Bits(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 比较两个Bits对象是否相等
|
* 比较两个Bits对象是否相等
|
||||||
|
* @param other 另一个Bits对象
|
||||||
|
* @returns 是否相等
|
||||||
*/
|
*/
|
||||||
public equals(other: Bits): boolean {
|
public equals(other: Bits): boolean {
|
||||||
return this._value === other._value;
|
return this._value.equals(other._value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取最高位的索引
|
* 获取最高位的索引
|
||||||
|
* @returns 最高位的索引,如果为空则返回-1
|
||||||
*/
|
*/
|
||||||
public getHighestBitIndex(): number {
|
public getHighestBitIndex(): number {
|
||||||
if (this._value === 0n) {
|
if (this._value.isZero()) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let index = 0;
|
let index = 0;
|
||||||
let value = this._value;
|
let value = this._value.clone();
|
||||||
|
|
||||||
while (value > 1n) {
|
while (!value.shiftRight(1).isZero()) {
|
||||||
value >>= 1n;
|
value = value.shiftRight(1);
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,17 +300,19 @@ export class Bits {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取最低位的索引
|
* 获取最低位的索引
|
||||||
|
* @returns 最低位的索引,如果为空则返回-1
|
||||||
*/
|
*/
|
||||||
public getLowestBitIndex(): number {
|
public getLowestBitIndex(): number {
|
||||||
if (this._value === 0n) {
|
if (this._value.isZero()) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let index = 0;
|
let index = 0;
|
||||||
let value = this._value;
|
let value = this._value.clone();
|
||||||
|
const one = BigIntFactory.one();
|
||||||
|
|
||||||
while ((value & 1n) === 0n) {
|
while (value.and(one).isZero()) {
|
||||||
value >>= 1n;
|
value = value.shiftRight(1);
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { BitMaskOptimizer } from '../../../src/ECS/Core/BitMaskOptimizer';
|
import { BitMaskOptimizer } from '../../../src/ECS/Core/BitMaskOptimizer';
|
||||||
|
import { BigIntFactory } from '../../../src/ECS/Utils/BigIntCompatibility';
|
||||||
|
|
||||||
describe('BitMaskOptimizer - 位掩码优化器测试', () => {
|
describe('BitMaskOptimizer - 位掩码优化器测试', () => {
|
||||||
let optimizer: BitMaskOptimizer;
|
let optimizer: BitMaskOptimizer;
|
||||||
@@ -58,15 +59,15 @@ describe('BitMaskOptimizer - 位掩码优化器测试', () => {
|
|||||||
|
|
||||||
it('应该能够创建单个组件的掩码', () => {
|
it('应该能够创建单个组件的掩码', () => {
|
||||||
const mask = optimizer.createSingleComponentMask('Transform');
|
const mask = optimizer.createSingleComponentMask('Transform');
|
||||||
expect(mask).toBe(1n);
|
expect(mask.toString()).toBe('1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('不同组件应该有不同的掩码', () => {
|
it('不同组件应该有不同的掩码', () => {
|
||||||
const transformMask = optimizer.createSingleComponentMask('Transform');
|
const transformMask = optimizer.createSingleComponentMask('Transform');
|
||||||
const velocityMask = optimizer.createSingleComponentMask('Velocity');
|
const velocityMask = optimizer.createSingleComponentMask('Velocity');
|
||||||
|
|
||||||
expect(transformMask).toBe(1n);
|
expect(transformMask.toString()).toBe('1');
|
||||||
expect(velocityMask).toBe(2n);
|
expect(velocityMask.toString()).toBe('2');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('创建未注册组件的掩码应该抛出错误', () => {
|
it('创建未注册组件的掩码应该抛出错误', () => {
|
||||||
@@ -91,7 +92,7 @@ describe('BitMaskOptimizer - 位掩码优化器测试', () => {
|
|||||||
|
|
||||||
it('应该能够创建多个组件的组合掩码', () => {
|
it('应该能够创建多个组件的组合掩码', () => {
|
||||||
const mask = optimizer.createCombinedMask(['Transform', 'Velocity']);
|
const mask = optimizer.createCombinedMask(['Transform', 'Velocity']);
|
||||||
expect(mask).toBe(3n); // 1n | 2n = 3n
|
expect(mask.toString()).toBe('3'); // 1 | 2 = 3
|
||||||
});
|
});
|
||||||
|
|
||||||
it('组件顺序不应该影响掩码结果', () => {
|
it('组件顺序不应该影响掩码结果', () => {
|
||||||
@@ -102,12 +103,12 @@ describe('BitMaskOptimizer - 位掩码优化器测试', () => {
|
|||||||
|
|
||||||
it('三个组件的组合掩码应该正确', () => {
|
it('三个组件的组合掩码应该正确', () => {
|
||||||
const mask = optimizer.createCombinedMask(['Transform', 'Velocity', 'Health']);
|
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掩码', () => {
|
it('空数组应该返回0掩码', () => {
|
||||||
const mask = optimizer.createCombinedMask([]);
|
const mask = optimizer.createCombinedMask([]);
|
||||||
expect(mask).toBe(0n);
|
expect(mask.isZero()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('包含未注册组件应该抛出错误', () => {
|
it('包含未注册组件应该抛出错误', () => {
|
||||||
@@ -181,7 +182,7 @@ describe('BitMaskOptimizer - 位掩码优化器测试', () => {
|
|||||||
const originalMask = optimizer.createSingleComponentMask('Transform');
|
const originalMask = optimizer.createSingleComponentMask('Transform');
|
||||||
const newMask = optimizer.removeComponentFromMask(originalMask, 'Velocity');
|
const newMask = optimizer.removeComponentFromMask(originalMask, 'Velocity');
|
||||||
|
|
||||||
expect(newMask).toBe(originalMask);
|
expect(newMask.equals(originalMask)).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -203,7 +204,7 @@ describe('BitMaskOptimizer - 位掩码优化器测试', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('空掩码应该返回空数组', () => {
|
it('空掩码应该返回空数组', () => {
|
||||||
const componentNames = optimizer.maskToComponentNames(0n);
|
const componentNames = optimizer.maskToComponentNames(BigIntFactory.zero());
|
||||||
expect(componentNames).toEqual([]);
|
expect(componentNames).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -218,7 +219,7 @@ describe('BitMaskOptimizer - 位掩码优化器测试', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('空掩码的组件数量应该为0', () => {
|
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');
|
const mask = optimizer.createSingleComponentMask('Component63');
|
||||||
expect(mask).toBe(1n << 63n);
|
expect(mask.toString()).toBe(BigIntFactory.one().shiftLeft(63).toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('空组件名称数组的组合掩码', () => {
|
it('空组件名称数组的组合掩码', () => {
|
||||||
const mask = optimizer.createCombinedMask([]);
|
const mask = optimizer.createCombinedMask([]);
|
||||||
expect(mask).toBe(0n);
|
expect(mask.isZero()).toBe(true);
|
||||||
expect(optimizer.getComponentCount(mask)).toBe(0);
|
expect(optimizer.getComponentCount(mask)).toBe(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
ComponentType
|
ComponentType
|
||||||
} from '../../../src/ECS/Core/ComponentStorage';
|
} from '../../../src/ECS/Core/ComponentStorage';
|
||||||
import { Component } from '../../../src/ECS/Component';
|
import { Component } from '../../../src/ECS/Component';
|
||||||
|
import { BigIntFactory } from '../../../src/ECS/Utils/BigIntCompatibility';
|
||||||
|
|
||||||
// 测试组件类
|
// 测试组件类
|
||||||
class TestComponent extends Component {
|
class TestComponent extends Component {
|
||||||
@@ -93,8 +94,8 @@ describe('ComponentRegistry - 组件注册表测试', () => {
|
|||||||
const mask1 = ComponentRegistry.getBitMask(TestComponent);
|
const mask1 = ComponentRegistry.getBitMask(TestComponent);
|
||||||
const mask2 = ComponentRegistry.getBitMask(PositionComponent);
|
const mask2 = ComponentRegistry.getBitMask(PositionComponent);
|
||||||
|
|
||||||
expect(mask1).toBe(BigInt(1)); // 2^0
|
expect(mask1.toString()).toBe('1'); // 2^0
|
||||||
expect(mask2).toBe(BigInt(2)); // 2^1
|
expect(mask2.toString()).toBe('2'); // 2^1
|
||||||
});
|
});
|
||||||
|
|
||||||
test('应该能够获取组件的位索引', () => {
|
test('应该能够获取组件的位索引', () => {
|
||||||
@@ -491,13 +492,12 @@ describe('ComponentStorageManager - 组件存储管理器测试', () => {
|
|||||||
const mask = manager.getComponentMask(1);
|
const mask = manager.getComponentMask(1);
|
||||||
|
|
||||||
// 应该包含TestComponent(位0)和PositionComponent(位1)的掩码
|
// 应该包含TestComponent(位0)和PositionComponent(位1)的掩码
|
||||||
const expectedMask = BigInt(1) | BigInt(2); // 0b11
|
expect(mask.toString()).toBe('3'); // 1 | 2 = 3
|
||||||
expect(mask).toBe(expectedMask);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('没有组件的实体应该有零掩码', () => {
|
test('没有组件的实体应该有零掩码', () => {
|
||||||
const mask = manager.getComponentMask(999);
|
const mask = manager.getComponentMask(999);
|
||||||
expect(mask).toBe(BigInt(0));
|
expect(mask.isZero()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('添加和移除组件应该更新掩码', () => {
|
test('添加和移除组件应该更新掩码', () => {
|
||||||
@@ -506,15 +506,15 @@ describe('ComponentStorageManager - 组件存储管理器测试', () => {
|
|||||||
|
|
||||||
manager.addComponent(1, new TestComponent(100));
|
manager.addComponent(1, new TestComponent(100));
|
||||||
let mask = manager.getComponentMask(1);
|
let mask = manager.getComponentMask(1);
|
||||||
expect(mask).toBe(BigInt(1));
|
expect(mask.toString()).toBe('1');
|
||||||
|
|
||||||
manager.addComponent(1, new PositionComponent(10, 20));
|
manager.addComponent(1, new PositionComponent(10, 20));
|
||||||
mask = manager.getComponentMask(1);
|
mask = manager.getComponentMask(1);
|
||||||
expect(mask).toBe(BigInt(3)); // 0b11
|
expect(mask.toString()).toBe('3'); // 0b11
|
||||||
|
|
||||||
manager.removeComponent(1, TestComponent);
|
manager.removeComponent(1, TestComponent);
|
||||||
mask = manager.getComponentMask(1);
|
mask = manager.getComponentMask(1);
|
||||||
expect(mask).toBe(BigInt(2)); // 0b10
|
expect(mask.toString()).toBe('2'); // 0b10
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
338
tests/ECS/Utils/BigIntCompatibility.test.ts
Normal file
338
tests/ECS/Utils/BigIntCompatibility.test.ts
Normal file
@@ -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');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Bits } from '../../../src/ECS/Utils/Bits';
|
import { Bits } from '../../../src/ECS/Utils/Bits';
|
||||||
|
import { BigIntFactory } from '../../../src/ECS/Utils/BigIntCompatibility';
|
||||||
|
|
||||||
describe('Bits - 高性能位操作类测试', () => {
|
describe('Bits - 高性能位操作类测试', () => {
|
||||||
let bits: Bits;
|
let bits: Bits;
|
||||||
@@ -11,12 +12,12 @@ describe('Bits - 高性能位操作类测试', () => {
|
|||||||
it('应该能够创建空的Bits对象', () => {
|
it('应该能够创建空的Bits对象', () => {
|
||||||
expect(bits).toBeDefined();
|
expect(bits).toBeDefined();
|
||||||
expect(bits.isEmpty()).toBe(true);
|
expect(bits.isEmpty()).toBe(true);
|
||||||
expect(bits.getValue()).toBe(0n);
|
expect(bits.getValue().isZero()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('应该能够使用初始值创建Bits对象', () => {
|
it('应该能够使用初始值创建Bits对象', () => {
|
||||||
const bitsWithValue = new Bits(5n); // 二进制: 101
|
const bitsWithValue = new Bits(BigIntFactory.create(5)); // 二进制: 101
|
||||||
expect(bitsWithValue.getValue()).toBe(5n);
|
expect(bitsWithValue.getValue().toString()).toBe('5');
|
||||||
expect(bitsWithValue.isEmpty()).toBe(false);
|
expect(bitsWithValue.isEmpty()).toBe(false);
|
||||||
expect(bitsWithValue.get(0)).toBe(true); // 第0位
|
expect(bitsWithValue.get(0)).toBe(true); // 第0位
|
||||||
expect(bitsWithValue.get(1)).toBe(false); // 第1位
|
expect(bitsWithValue.get(1)).toBe(false); // 第1位
|
||||||
@@ -25,7 +26,7 @@ describe('Bits - 高性能位操作类测试', () => {
|
|||||||
|
|
||||||
it('默认构造函数应该创建值为0的对象', () => {
|
it('默认构造函数应该创建值为0的对象', () => {
|
||||||
const defaultBits = new Bits();
|
const defaultBits = new Bits();
|
||||||
expect(defaultBits.getValue()).toBe(0n);
|
expect(defaultBits.getValue().isZero()).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -33,22 +34,22 @@ describe('Bits - 高性能位操作类测试', () => {
|
|||||||
it('应该能够设置指定位置的位', () => {
|
it('应该能够设置指定位置的位', () => {
|
||||||
bits.set(0);
|
bits.set(0);
|
||||||
expect(bits.get(0)).toBe(true);
|
expect(bits.get(0)).toBe(true);
|
||||||
expect(bits.getValue()).toBe(1n);
|
expect(bits.getValue().toString()).toBe('1');
|
||||||
|
|
||||||
bits.set(3);
|
bits.set(3);
|
||||||
expect(bits.get(3)).toBe(true);
|
expect(bits.get(3)).toBe(true);
|
||||||
expect(bits.getValue()).toBe(9n); // 1001 in binary
|
expect(bits.getValue().toString()).toBe('9'); // 1001 in binary
|
||||||
});
|
});
|
||||||
|
|
||||||
it('应该能够清除指定位置的位', () => {
|
it('应该能够清除指定位置的位', () => {
|
||||||
bits.set(0);
|
bits.set(0);
|
||||||
bits.set(1);
|
bits.set(1);
|
||||||
bits.set(2);
|
bits.set(2);
|
||||||
expect(bits.getValue()).toBe(7n); // 111 in binary
|
expect(bits.getValue().toString()).toBe('7'); // 111 in binary
|
||||||
|
|
||||||
bits.clear(1);
|
bits.clear(1);
|
||||||
expect(bits.get(1)).toBe(false);
|
expect(bits.get(1)).toBe(false);
|
||||||
expect(bits.getValue()).toBe(5n); // 101 in binary
|
expect(bits.getValue().toString()).toBe('5'); // 101 in binary
|
||||||
});
|
});
|
||||||
|
|
||||||
it('重复设置同一位应该保持不变', () => {
|
it('重复设置同一位应该保持不变', () => {
|
||||||
@@ -56,12 +57,12 @@ describe('Bits - 高性能位操作类测试', () => {
|
|||||||
const value1 = bits.getValue();
|
const value1 = bits.getValue();
|
||||||
bits.set(0);
|
bits.set(0);
|
||||||
const value2 = bits.getValue();
|
const value2 = bits.getValue();
|
||||||
expect(value1).toBe(value2);
|
expect(value1.equals(value2)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('清除未设置的位应该安全', () => {
|
it('清除未设置的位应该安全', () => {
|
||||||
bits.clear(5);
|
bits.clear(5);
|
||||||
expect(bits.getValue()).toBe(0n);
|
expect(bits.getValue().isZero()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('设置负索引应该抛出错误', () => {
|
it('设置负索引应该抛出错误', () => {
|
||||||
@@ -122,7 +123,7 @@ describe('Bits - 高性能位操作类测试', () => {
|
|||||||
|
|
||||||
it('AND运算应该正确', () => {
|
it('AND运算应该正确', () => {
|
||||||
const result = bits.and(otherBits);
|
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(2)).toBe(true);
|
||||||
expect(result.get(0)).toBe(false);
|
expect(result.get(0)).toBe(false);
|
||||||
expect(result.get(1)).toBe(false);
|
expect(result.get(1)).toBe(false);
|
||||||
@@ -130,7 +131,7 @@ describe('Bits - 高性能位操作类测试', () => {
|
|||||||
|
|
||||||
it('OR运算应该正确', () => {
|
it('OR运算应该正确', () => {
|
||||||
const result = bits.or(otherBits);
|
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(0)).toBe(true);
|
||||||
expect(result.get(1)).toBe(true);
|
expect(result.get(1)).toBe(true);
|
||||||
expect(result.get(2)).toBe(true);
|
expect(result.get(2)).toBe(true);
|
||||||
@@ -140,7 +141,7 @@ describe('Bits - 高性能位操作类测试', () => {
|
|||||||
|
|
||||||
it('XOR运算应该正确', () => {
|
it('XOR运算应该正确', () => {
|
||||||
const result = bits.xor(otherBits);
|
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(0)).toBe(true);
|
||||||
expect(result.get(1)).toBe(true);
|
expect(result.get(1)).toBe(true);
|
||||||
expect(result.get(2)).toBe(false); // 相同位XOR为0
|
expect(result.get(2)).toBe(false); // 相同位XOR为0
|
||||||
@@ -149,16 +150,16 @@ describe('Bits - 高性能位操作类测试', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('NOT运算应该正确', () => {
|
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位
|
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位应该正确', () => {
|
it('NOT运算默认64位应该正确', () => {
|
||||||
const simpleBits = new Bits(1n);
|
const simpleBits = new Bits(BigIntFactory.create(1));
|
||||||
const result = simpleBits.not();
|
const result = simpleBits.not();
|
||||||
const expected = (1n << 64n) - 2n; // 64位全1减去最低位
|
const expected = BigIntFactory.one().shiftLeft(64).valueOf() - 2; // 64位全1减去最低位
|
||||||
expect(result.getValue()).toBe(expected);
|
expect(result.getValue().valueOf()).toBe(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -253,7 +254,7 @@ describe('Bits - 高性能位操作类测试', () => {
|
|||||||
expect(bits.isEmpty()).toBe(false);
|
expect(bits.isEmpty()).toBe(false);
|
||||||
bits.clearAll();
|
bits.clearAll();
|
||||||
expect(bits.isEmpty()).toBe(true);
|
expect(bits.isEmpty()).toBe(true);
|
||||||
expect(bits.getValue()).toBe(0n);
|
expect(bits.getValue().isZero()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('clearAll后应该能重新设置位', () => {
|
it('clearAll后应该能重新设置位', () => {
|
||||||
@@ -275,14 +276,14 @@ describe('Bits - 高性能位操作类测试', () => {
|
|||||||
const newBits = new Bits();
|
const newBits = new Bits();
|
||||||
newBits.copyFrom(bits);
|
newBits.copyFrom(bits);
|
||||||
|
|
||||||
expect(newBits.getValue()).toBe(bits.getValue());
|
expect(newBits.getValue().equals(bits.getValue())).toBe(true);
|
||||||
expect(newBits.equals(bits)).toBe(true);
|
expect(newBits.equals(bits)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('clone应该创建相同的副本', () => {
|
it('clone应该创建相同的副本', () => {
|
||||||
const clonedBits = bits.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.equals(bits)).toBe(true);
|
||||||
expect(clonedBits).not.toBe(bits); // 应该是不同的对象
|
expect(clonedBits).not.toBe(bits); // 应该是不同的对象
|
||||||
});
|
});
|
||||||
@@ -298,12 +299,12 @@ describe('Bits - 高性能位操作类测试', () => {
|
|||||||
|
|
||||||
describe('值操作', () => {
|
describe('值操作', () => {
|
||||||
it('getValue和setValue应该正确工作', () => {
|
it('getValue和setValue应该正确工作', () => {
|
||||||
bits.setValue(42n);
|
bits.setValue(42);
|
||||||
expect(bits.getValue()).toBe(42n);
|
expect(bits.getValue().toString()).toBe('42');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('setValue应该正确反映在位操作中', () => {
|
it('setValue应该正确反映在位操作中', () => {
|
||||||
bits.setValue(5n); // 101 in binary
|
bits.setValue(5); // 101 in binary
|
||||||
expect(bits.get(0)).toBe(true);
|
expect(bits.get(0)).toBe(true);
|
||||||
expect(bits.get(1)).toBe(false);
|
expect(bits.get(1)).toBe(false);
|
||||||
expect(bits.get(2)).toBe(true);
|
expect(bits.get(2)).toBe(true);
|
||||||
@@ -312,7 +313,7 @@ describe('Bits - 高性能位操作类测试', () => {
|
|||||||
it('setValue为0应该清空所有位', () => {
|
it('setValue为0应该清空所有位', () => {
|
||||||
bits.set(1);
|
bits.set(1);
|
||||||
bits.set(2);
|
bits.set(2);
|
||||||
bits.setValue(0n);
|
bits.setValue(0);
|
||||||
expect(bits.isEmpty()).toBe(true);
|
expect(bits.isEmpty()).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -351,24 +352,24 @@ describe('Bits - 高性能位操作类测试', () => {
|
|||||||
|
|
||||||
it('fromBinaryString应该正确解析', () => {
|
it('fromBinaryString应该正确解析', () => {
|
||||||
const parsedBits = Bits.fromBinaryString('10101');
|
const parsedBits = Bits.fromBinaryString('10101');
|
||||||
expect(parsedBits.getValue()).toBe(21n);
|
expect(parsedBits.getValue().toString()).toBe('21');
|
||||||
expect(parsedBits.equals(bits)).toBe(true);
|
expect(parsedBits.equals(bits)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fromBinaryString应该处理带空格的字符串', () => {
|
it('fromBinaryString应该处理带空格的字符串', () => {
|
||||||
const parsedBits = Bits.fromBinaryString('0001 0101');
|
const parsedBits = Bits.fromBinaryString('0001 0101');
|
||||||
expect(parsedBits.getValue()).toBe(21n);
|
expect(parsedBits.getValue().toString()).toBe('21');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fromHexString应该正确解析', () => {
|
it('fromHexString应该正确解析', () => {
|
||||||
const parsedBits = Bits.fromHexString('0x15');
|
const parsedBits = Bits.fromHexString('0x15');
|
||||||
expect(parsedBits.getValue()).toBe(21n);
|
expect(parsedBits.getValue().toString()).toBe('21');
|
||||||
expect(parsedBits.equals(bits)).toBe(true);
|
expect(parsedBits.equals(bits)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fromHexString应该处理不带0x前缀的字符串', () => {
|
it('fromHexString应该处理不带0x前缀的字符串', () => {
|
||||||
const parsedBits = Bits.fromHexString('15');
|
const parsedBits = Bits.fromHexString('15');
|
||||||
expect(parsedBits.getValue()).toBe(21n);
|
expect(parsedBits.getValue().toString()).toBe('21');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -515,7 +516,7 @@ describe('Bits - 高性能位操作类测试', () => {
|
|||||||
|
|
||||||
describe('边界情况和错误处理', () => {
|
describe('边界情况和错误处理', () => {
|
||||||
it('应该处理0值的各种操作', () => {
|
it('应该处理0值的各种操作', () => {
|
||||||
const zeroBits = new Bits(0n);
|
const zeroBits = new Bits(BigIntFactory.zero());
|
||||||
expect(zeroBits.isEmpty()).toBe(true);
|
expect(zeroBits.isEmpty()).toBe(true);
|
||||||
expect(zeroBits.cardinality()).toBe(0);
|
expect(zeroBits.cardinality()).toBe(0);
|
||||||
expect(zeroBits.getHighestBitIndex()).toBe(-1);
|
expect(zeroBits.getHighestBitIndex()).toBe(-1);
|
||||||
@@ -523,7 +524,7 @@ describe('Bits - 高性能位操作类测试', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('应该处理最大BigInt值', () => {
|
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.isEmpty()).toBe(false);
|
||||||
expect(maxBits.cardinality()).toBeGreaterThan(0);
|
expect(maxBits.cardinality()).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user