1+ import { BitSet } from "./bit-set" ;
2+
13/**
24 * Unique symbol brand for associating component type information with EntityId
35 */
@@ -460,6 +462,11 @@ export interface ComponentOptions {
460462
461463const ComponentOptions : Map < ComponentId < any > , ComponentOptions > = new Map ( ) ;
462464
465+ // BitSets for fast component option checks (Component ID range: 1-1023)
466+ const exclusiveFlags = new BitSet ( COMPONENT_ID_MAX + 1 ) ;
467+ const cascadeDeleteFlags = new BitSet ( COMPONENT_ID_MAX + 1 ) ;
468+ const dontFragmentFlags = new BitSet ( COMPONENT_ID_MAX + 1 ) ;
469+
463470/**
464471 * Allocate a new component ID from the global allocator.
465472 * @param nameOrOptions Optional name for the component (for serialization/debugging) or options object
@@ -501,6 +508,10 @@ export function component<T = void>(nameOrOptions?: string | ComponentOptions):
501508 // Register options if provided
502509 if ( options ) {
503510 ComponentOptions . set ( id , options ) ;
511+ // Set bitset flags for fast lookup
512+ if ( options . exclusive ) exclusiveFlags . set ( id ) ;
513+ if ( options . cascadeDelete ) cascadeDeleteFlags . set ( id ) ;
514+ if ( options . dontFragment ) dontFragmentFlags . set ( id ) ;
504515 }
505516
506517 return id ;
@@ -538,7 +549,7 @@ export function getComponentOptions(id: ComponentId<any>): ComponentOptions | un
538549 * @returns true if the component is exclusive, false otherwise
539550 */
540551export function isExclusiveComponent ( id : ComponentId < any > ) : boolean {
541- return ComponentOptions . get ( id ) ?. exclusive ?? false ;
552+ return exclusiveFlags . has ( id ) ;
542553}
543554
544555/**
@@ -547,7 +558,7 @@ export function isExclusiveComponent(id: ComponentId<any>): boolean {
547558 * @returns true if the component is cascade delete, false otherwise
548559 */
549560export function isCascadeDeleteComponent ( id : ComponentId < any > ) : boolean {
550- return ComponentOptions . get ( id ) ?. cascadeDelete ?? false ;
561+ return cascadeDeleteFlags . has ( id ) ;
551562}
552563
553564/**
@@ -556,5 +567,143 @@ export function isCascadeDeleteComponent(id: ComponentId<any>): boolean {
556567 * @returns true if the component is dontFragment, false otherwise
557568 */
558569export function isDontFragmentComponent ( id : ComponentId < any > ) : boolean {
559- return ComponentOptions . get ( id ) ?. dontFragment ?? false ;
570+ return dontFragmentFlags . has ( id ) ;
571+ }
572+
573+ /**
574+ * Check if a relation ID is a dontFragment relation (entity-relation or component-relation with dontFragment component)
575+ * This is an optimized function that avoids the overhead of getDetailedIdType
576+ * @param id The entity/relation ID to check
577+ * @returns true if this is a dontFragment relation, false otherwise
578+ */
579+ export function isDontFragmentRelation ( id : EntityId < any > ) : boolean {
580+ // Only relation IDs are negative; non-relations return false immediately
581+ if ( id >= 0 ) return false ;
582+
583+ // Extract componentId directly from the relation encoding: -(componentId * RELATION_SHIFT + targetId)
584+ // componentId = floor(absId / RELATION_SHIFT)
585+ const absId = - id ;
586+ const componentId = Math . floor ( absId / RELATION_SHIFT ) ;
587+
588+ // Wildcard relations (targetId === 0) are not considered dontFragment relations for this check
589+ const targetId = absId % RELATION_SHIFT ;
590+ if ( targetId === WILDCARD_TARGET_ID ) return false ;
591+
592+ // Check if componentId is valid and has dontFragment flag
593+ return componentId >= 1 && componentId <= COMPONENT_ID_MAX && dontFragmentFlags . has ( componentId ) ;
594+ }
595+
596+ /**
597+ * Check if an ID is a wildcard relation with dontFragment component
598+ * This is an optimized function for filtering archetype component types
599+ * @param id The entity/relation ID to check
600+ * @returns true if this is a wildcard relation with dontFragment component, false otherwise
601+ */
602+ export function isDontFragmentWildcard ( id : EntityId < any > ) : boolean {
603+ // Only relation IDs are negative
604+ if ( id >= 0 ) return false ;
605+
606+ const absId = - id ;
607+ const targetId = absId % RELATION_SHIFT ;
608+
609+ // Must be a wildcard relation (targetId === 0)
610+ if ( targetId !== WILDCARD_TARGET_ID ) return false ;
611+
612+ const componentId = Math . floor ( absId / RELATION_SHIFT ) ;
613+ return componentId >= 1 && componentId <= COMPONENT_ID_MAX && dontFragmentFlags . has ( componentId ) ;
614+ }
615+
616+ /**
617+ * Check if a relation ID is an exclusive relation (entity-relation or component-relation with exclusive component)
618+ * This avoids the full getDetailedIdType overhead for hot paths
619+ * @param id The entity/relation ID to check
620+ * @returns true if this is an exclusive relation, false otherwise
621+ */
622+ export function isExclusiveRelation ( id : EntityId < any > ) : boolean {
623+ if ( id >= 0 ) return false ;
624+ const absId = - id ;
625+ const targetId = absId % RELATION_SHIFT ;
626+ if ( targetId === WILDCARD_TARGET_ID ) return false ; // not an exclusive-specific relation
627+ const componentId = Math . floor ( absId / RELATION_SHIFT ) ;
628+ return componentId >= 1 && componentId <= COMPONENT_ID_MAX && exclusiveFlags . has ( componentId ) ;
629+ }
630+
631+ /**
632+ * Check if a relation ID is a wildcard relation with exclusive component
633+ */
634+ export function isExclusiveWildcard ( id : EntityId < any > ) : boolean {
635+ if ( id >= 0 ) return false ;
636+ const absId = - id ;
637+ const targetId = absId % RELATION_SHIFT ;
638+ if ( targetId !== WILDCARD_TARGET_ID ) return false ;
639+ const componentId = Math . floor ( absId / RELATION_SHIFT ) ;
640+ return componentId >= 1 && componentId <= COMPONENT_ID_MAX && exclusiveFlags . has ( componentId ) ;
641+ }
642+
643+ /**
644+ * Check if a relation ID is a cascade delete entity-relation
645+ * This is an optimized function that avoids the overhead of getDetailedIdType
646+ * Note: Cascade delete only applies to entity-relations (not component-relations or wildcards)
647+ * @param id The entity/relation ID to check
648+ * @returns true if this is an entity-relation with cascade delete, false otherwise
649+ */
650+ export function isCascadeDeleteRelation ( id : EntityId < any > ) : boolean {
651+ if ( id >= 0 ) return false ;
652+ const absId = - id ;
653+ const targetId = absId % RELATION_SHIFT ;
654+ // Wildcard relations (targetId === 0) don't cascade
655+ if ( targetId === WILDCARD_TARGET_ID ) return false ;
656+ // Only entity-relations cascade (targetId >= ENTITY_ID_START)
657+ if ( targetId < ENTITY_ID_START ) return false ;
658+ const componentId = Math . floor ( absId / RELATION_SHIFT ) ;
659+ return componentId >= 1 && componentId <= COMPONENT_ID_MAX && cascadeDeleteFlags . has ( componentId ) ;
660+ }
661+
662+ /**
663+ * Get the componentId from a relation ID without fully decoding the relation.
664+ * Returns undefined for non-relation IDs or invalid component IDs.
665+ */
666+ export function getComponentIdFromRelationId < T > ( id : EntityId < T > ) : ComponentId < T > | undefined {
667+ if ( id >= 0 ) return undefined ;
668+ const absId = - id ;
669+ const componentId = Math . floor ( absId / RELATION_SHIFT ) ;
670+ if ( componentId < 1 || componentId > COMPONENT_ID_MAX ) return undefined ;
671+ return componentId as ComponentId < T > ;
672+ }
673+
674+ /**
675+ * Get the targetId from a relation ID without fully decoding the relation.
676+ * Returns undefined for non-relation IDs.
677+ */
678+ export function getTargetIdFromRelationId ( id : EntityId < any > ) : EntityId < any > | undefined {
679+ if ( id >= 0 ) return undefined ;
680+ const absId = - id ;
681+ return ( absId % RELATION_SHIFT ) as EntityId < any > ;
682+ }
683+
684+ /**
685+ * Check if an ID is an entity-relation (relation targeting an entity, not a component or wildcard)
686+ */
687+ export function isEntityRelation ( id : EntityId < any > ) : boolean {
688+ if ( id >= 0 ) return false ;
689+ const absId = - id ;
690+ const targetId = absId % RELATION_SHIFT ;
691+ return targetId >= ENTITY_ID_START ;
692+ }
693+
694+ /**
695+ * Check if an ID is a component-relation (relation targeting a component)
696+ */
697+ export function isComponentRelation ( id : EntityId < any > ) : boolean {
698+ if ( id >= 0 ) return false ;
699+ const absId = - id ;
700+ const targetId = absId % RELATION_SHIFT ;
701+ return targetId >= 1 && targetId <= COMPONENT_ID_MAX ;
702+ }
703+
704+ /**
705+ * Check if an ID is any type of relation (entity, component, or wildcard)
706+ */
707+ export function isAnyRelation ( id : EntityId < any > ) : boolean {
708+ return id < 0 ;
560709}
0 commit comments