diff --git a/packages/core/src/render3/di.ts b/packages/core/src/render3/di.ts index b7120a6e26..707a5d5ec9 100644 --- a/packages/core/src/render3/di.ts +++ b/packages/core/src/render3/di.ts @@ -44,6 +44,7 @@ const NG_ELEMENT_ID = '__NG_ELEMENT_ID__'; * the existence of a directive. */ const BLOOM_SIZE = 256; +const BLOOM_MASK = BLOOM_SIZE - 1; /** Counter used to generate unique IDs for directives. */ let nextNgElementId = 0; @@ -56,7 +57,7 @@ let nextNgElementId = 0; * @param type The directive to register */ export function bloomAdd(injector: LInjector, type: Type): void { - let id = (type as any)[NG_ELEMENT_ID]; + let id: number|undefined = (type as any)[NG_ELEMENT_ID]; // Set a unique ID on the directive type, so if something tries to inject the directive, // we can easily retrieve the ID and hash it into the bloom bit that should be checked. @@ -66,9 +67,7 @@ export function bloomAdd(injector: LInjector, type: Type): void { // We only have BLOOM_SIZE (256) slots in our bloom filter (8 buckets * 32 bits each), // so all unique IDs must be modulo-ed into a number from 0 - 255 to fit into the filter. - // This means that after 255, some directives will share slots, leading to some false positives - // when checking for a directive's presence. - const bloomBit = id % BLOOM_SIZE; + const bloomBit = id & BLOOM_MASK; // Create a mask that targets the specific bit associated with the directive. // JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding @@ -77,13 +76,16 @@ export function bloomAdd(injector: LInjector, type: Type): void { // Use the raw bloomBit number to determine which bloom filter bucket we should check // e.g: bf0 = [0 - 31], bf1 = [32 - 63], bf2 = [64 - 95], bf3 = [96 - 127], etc - if (bloomBit < 128) { - // Then use the mask to flip on the bit (0-31) associated with the directive in that bucket - bloomBit < 64 ? (bloomBit < 32 ? (injector.bf0 |= mask) : (injector.bf1 |= mask)) : - (bloomBit < 96 ? (injector.bf2 |= mask) : (injector.bf3 |= mask)); + const b7 = bloomBit & 0x80; + const b6 = bloomBit & 0x40; + const b5 = bloomBit & 0x20; + + if (b7) { + b6 ? (b5 ? (injector.bf7 |= mask) : (injector.bf6 |= mask)) : + (b5 ? (injector.bf5 |= mask) : (injector.bf4 |= mask)) } else { - bloomBit < 192 ? (bloomBit < 160 ? (injector.bf4 |= mask) : (injector.bf5 |= mask)) : - (bloomBit < 224 ? (injector.bf6 |= mask) : (injector.bf7 |= mask)); + b6 ? (b5 ? (injector.bf3 |= mask) : (injector.bf2 |= mask)) : + (b5 ? (injector.bf1 |= mask) : (injector.bf0 |= mask)) } } @@ -428,7 +430,7 @@ function searchMatchesQueuedForCreation(node: LNode, token: any): T|null { */ function bloomHashBit(token: Type| InjectionToken): number|null { let id: number|undefined = (token as any)[NG_ELEMENT_ID]; - return typeof id === 'number' ? id % BLOOM_SIZE : null; + return typeof id === 'number' ? id & BLOOM_MASK : null; } /** @@ -457,46 +459,54 @@ export function bloomFindPossibleInjector( // JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding // to bit positions 0 - 31 in a 32 bit integer. const mask = 1 << bloomBit; + const b7 = bloomBit & 0x80; + const b6 = bloomBit & 0x40; + const b5 = bloomBit & 0x20; // Traverse up the injector tree until we find a potential match or until we know there *isn't* a // match. let injector: LInjector|null = - flags & InjectFlags.SkipSelf ? startInjector.parent ! : startInjector; + flags & InjectFlags.SkipSelf ? startInjector.parent : startInjector; + while (injector) { // Our bloom filter size is 256 bits, which is eight 32-bit bloom filter buckets: // bf0 = [0 - 31], bf1 = [32 - 63], bf2 = [64 - 95], bf3 = [96 - 127], etc. // Get the bloom filter value from the appropriate bucket based on the directive's bloomBit. let value: number; - if (bloomBit < 128) { - value = bloomBit < 64 ? (bloomBit < 32 ? injector.bf0 : injector.bf1) : - (bloomBit < 96 ? injector.bf2 : injector.bf3); + + if (b7) { + value = b6 ? (b5 ? injector.bf7 : injector.bf6) : (b5 ? injector.bf5 : injector.bf4); } else { - value = bloomBit < 192 ? (bloomBit < 160 ? injector.bf4 : injector.bf5) : - (bloomBit < 224 ? injector.bf6 : injector.bf7); + value = b6 ? (b5 ? injector.bf3 : injector.bf2) : (b5 ? injector.bf1 : injector.bf0); } // If the bloom filter value has the bit corresponding to the directive's bloomBit flipped on, // this injector is a potential match. - if ((value & mask) === mask) { + if (value & mask) { return injector; - } else if (flags & InjectFlags.Self || flags & InjectFlags.Host && !sameHostView(injector)) { + } + + if (flags & InjectFlags.Self || flags & InjectFlags.Host && !sameHostView(injector)) { return null; } // If the current injector does not have the directive, check the bloom filters for the ancestor // injectors (cbf0 - cbf7). These filters capture *all* ancestor injectors. - if (bloomBit < 128) { - value = bloomBit < 64 ? (bloomBit < 32 ? injector.cbf0 : injector.cbf1) : - (bloomBit < 96 ? injector.cbf2 : injector.cbf3); + if (b7) { + value = b6 ? (b5 ? injector.cbf7 : injector.cbf6) : (b5 ? injector.cbf5 : injector.cbf4); } else { - value = bloomBit < 192 ? (bloomBit < 160 ? injector.cbf4 : injector.cbf5) : - (bloomBit < 224 ? injector.cbf6 : injector.cbf7); + value = b6 ? (b5 ? injector.cbf3 : injector.cbf2) : (b5 ? injector.cbf1 : injector.cbf0); } // If the ancestor bloom filter value has the bit corresponding to the directive, traverse up to // find the specific injector. If the ancestor bloom filter does not have the bit, we can abort. - injector = (value & mask) ? injector.parent : null; + if (value & mask) { + injector = injector.parent; + } else { + return null; + } } + return null; }