refactor(ivy): use bit operations in node injector (#25166)

PR Close #25166
This commit is contained in:
Victor Berchet 2018-07-27 12:44:58 -07:00 committed by Miško Hevery
parent 16f0ac38b8
commit 9ea112473b
1 changed files with 35 additions and 25 deletions

View File

@ -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<any>): 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<any>): 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<any>): 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<T>(node: LNode, token: any): T|null {
*/
function bloomHashBit(token: Type<any>| InjectionToken<any>): 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;
}