refactor(ivy): use bit operations in node injector (#25166)
PR Close #25166
This commit is contained in:
parent
16f0ac38b8
commit
9ea112473b
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue