parent
e55bf8fa79
commit
4e6ac185e5
|
@ -45,7 +45,7 @@ const NG_ELEMENT_ID = '__NG_ELEMENT_ID__';
|
||||||
* directives that will share slots, and thus, the fewer false positives when checking for
|
* directives that will share slots, and thus, the fewer false positives when checking for
|
||||||
* the existence of a directive.
|
* the existence of a directive.
|
||||||
*/
|
*/
|
||||||
const BLOOM_SIZE = 128;
|
const BLOOM_SIZE = 256;
|
||||||
|
|
||||||
/** Counter used to generate unique IDs for directives. */
|
/** Counter used to generate unique IDs for directives. */
|
||||||
let nextNgElementId = 0;
|
let nextNgElementId = 0;
|
||||||
|
@ -66,9 +66,9 @@ export function bloomAdd(injector: LInjector, type: Type<any>): void {
|
||||||
id = (type as any)[NG_ELEMENT_ID] = nextNgElementId++;
|
id = (type as any)[NG_ELEMENT_ID] = nextNgElementId++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We only have BLOOM_SIZE (128) slots in our bloom filter (4 buckets * 32 bits each),
|
// 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 - 127 to fit into the filter.
|
// so all unique IDs must be modulo-ed into a number from 0 - 255 to fit into the filter.
|
||||||
// This means that after 128, some directives will share slots, leading to some false positives
|
// This means that after 255, some directives will share slots, leading to some false positives
|
||||||
// when checking for a directive's presence.
|
// when checking for a directive's presence.
|
||||||
const bloomBit = id % BLOOM_SIZE;
|
const bloomBit = id % BLOOM_SIZE;
|
||||||
|
|
||||||
|
@ -78,20 +78,14 @@ export function bloomAdd(injector: LInjector, type: Type<any>): void {
|
||||||
const mask = 1 << bloomBit;
|
const mask = 1 << bloomBit;
|
||||||
|
|
||||||
// Use the raw bloomBit number to determine which bloom filter bucket we should check
|
// 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]
|
// e.g: bf0 = [0 - 31], bf1 = [32 - 63], bf2 = [64 - 95], bf3 = [96 - 127], etc
|
||||||
if (bloomBit < 64) {
|
if (bloomBit < 128) {
|
||||||
if (bloomBit < 32) {
|
|
||||||
// Then use the mask to flip on the bit (0-31) associated with the directive in that bucket
|
// Then use the mask to flip on the bit (0-31) associated with the directive in that bucket
|
||||||
injector.bf0 |= mask;
|
bloomBit < 64 ? (bloomBit < 32 ? (injector.bf0 |= mask) : (injector.bf1 |= mask)) :
|
||||||
|
(bloomBit < 96 ? (injector.bf2 |= mask) : (injector.bf3 |= mask));
|
||||||
} else {
|
} else {
|
||||||
injector.bf1 |= mask;
|
bloomBit < 192 ? (bloomBit < 160 ? (injector.bf4 |= mask) : (injector.bf5 |= mask)) :
|
||||||
}
|
(bloomBit < 224 ? (injector.bf6 |= mask) : (injector.bf7 |= mask));
|
||||||
} else {
|
|
||||||
if (bloomBit < 96) {
|
|
||||||
injector.bf2 |= mask;
|
|
||||||
} else {
|
|
||||||
injector.bf3 |= mask;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,10 +113,18 @@ export function getOrCreateNodeInjectorForNode(node: LElementNode | LContainerNo
|
||||||
bf1: 0,
|
bf1: 0,
|
||||||
bf2: 0,
|
bf2: 0,
|
||||||
bf3: 0,
|
bf3: 0,
|
||||||
|
bf4: 0,
|
||||||
|
bf5: 0,
|
||||||
|
bf6: 0,
|
||||||
|
bf7: 0,
|
||||||
cbf0: parentInjector == null ? 0 : parentInjector.cbf0 | parentInjector.bf0,
|
cbf0: parentInjector == null ? 0 : parentInjector.cbf0 | parentInjector.bf0,
|
||||||
cbf1: parentInjector == null ? 0 : parentInjector.cbf1 | parentInjector.bf1,
|
cbf1: parentInjector == null ? 0 : parentInjector.cbf1 | parentInjector.bf1,
|
||||||
cbf2: parentInjector == null ? 0 : parentInjector.cbf2 | parentInjector.bf2,
|
cbf2: parentInjector == null ? 0 : parentInjector.cbf2 | parentInjector.bf2,
|
||||||
cbf3: parentInjector == null ? 0 : parentInjector.cbf3 | parentInjector.bf3,
|
cbf3: parentInjector == null ? 0 : parentInjector.cbf3 | parentInjector.bf3,
|
||||||
|
cbf4: parentInjector == null ? 0 : parentInjector.cbf4 | parentInjector.bf4,
|
||||||
|
cbf5: parentInjector == null ? 0 : parentInjector.cbf5 | parentInjector.bf5,
|
||||||
|
cbf6: parentInjector == null ? 0 : parentInjector.cbf6 | parentInjector.bf6,
|
||||||
|
cbf7: parentInjector == null ? 0 : parentInjector.cbf7 | parentInjector.bf7,
|
||||||
injector: null,
|
injector: null,
|
||||||
templateRef: null,
|
templateRef: null,
|
||||||
viewContainerRef: null,
|
viewContainerRef: null,
|
||||||
|
@ -461,11 +463,17 @@ export function bloomFindPossibleInjector(startInjector: LInjector, bloomBit: nu
|
||||||
// match.
|
// match.
|
||||||
let injector: LInjector|null = startInjector;
|
let injector: LInjector|null = startInjector;
|
||||||
while (injector) {
|
while (injector) {
|
||||||
// Our bloom filter size is 128 bits, which is four 32-bit bloom filter buckets:
|
// 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]
|
// 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.
|
// Get the bloom filter value from the appropriate bucket based on the directive's bloomBit.
|
||||||
let value: number = bloomBit < 64 ? (bloomBit < 32 ? injector.bf0 : injector.bf1) :
|
let value: number;
|
||||||
|
if (bloomBit < 128) {
|
||||||
|
value = bloomBit < 64 ? (bloomBit < 32 ? injector.bf0 : injector.bf1) :
|
||||||
(bloomBit < 96 ? injector.bf2 : injector.bf3);
|
(bloomBit < 96 ? injector.bf2 : injector.bf3);
|
||||||
|
} else {
|
||||||
|
value = bloomBit < 192 ? (bloomBit < 160 ? injector.bf4 : injector.bf5) :
|
||||||
|
(bloomBit < 224 ? injector.bf6 : injector.bf7);
|
||||||
|
}
|
||||||
|
|
||||||
// If the bloom filter value has the bit corresponding to the directive's bloomBit flipped on,
|
// If the bloom filter value has the bit corresponding to the directive's bloomBit flipped on,
|
||||||
// this injector is a potential match.
|
// this injector is a potential match.
|
||||||
|
@ -474,9 +482,14 @@ export function bloomFindPossibleInjector(startInjector: LInjector, bloomBit: nu
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the current injector does not have the directive, check the bloom filters for the ancestor
|
// If the current injector does not have the directive, check the bloom filters for the ancestor
|
||||||
// injectors (cbf0 - cbf3). These filters capture *all* ancestor injectors.
|
// injectors (cbf0 - cbf7). These filters capture *all* ancestor injectors.
|
||||||
|
if (bloomBit < 128) {
|
||||||
value = bloomBit < 64 ? (bloomBit < 32 ? injector.cbf0 : injector.cbf1) :
|
value = bloomBit < 64 ? (bloomBit < 32 ? injector.cbf0 : injector.cbf1) :
|
||||||
(bloomBit < 96 ? injector.cbf2 : injector.cbf3);
|
(bloomBit < 96 ? injector.cbf2 : injector.cbf3);
|
||||||
|
} else {
|
||||||
|
value = bloomBit < 192 ? (bloomBit < 160 ? injector.cbf4 : injector.cbf5) :
|
||||||
|
(bloomBit < 224 ? injector.cbf6 : injector.cbf7);
|
||||||
|
}
|
||||||
|
|
||||||
// If the ancestor bloom filter value has the bit corresponding to the directive, traverse up to
|
// 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.
|
// find the specific injector. If the ancestor bloom filter does not have the bit, we can abort.
|
||||||
|
|
|
@ -35,9 +35,13 @@ export interface LInjector {
|
||||||
* array at this level unless it's probable the directive is in it.
|
* array at this level unless it's probable the directive is in it.
|
||||||
*
|
*
|
||||||
* - bf0: Check directive IDs 0-31 (IDs are % 128)
|
* - bf0: Check directive IDs 0-31 (IDs are % 128)
|
||||||
* - bf1: Check directive IDs 33-63
|
* - bf1: Check directive IDs 32-63
|
||||||
* - bf2: Check directive IDs 64-95
|
* - bf2: Check directive IDs 64-95
|
||||||
* - bf3: Check directive IDs 96-127
|
* - bf3: Check directive IDs 96-127
|
||||||
|
* - bf4: Check directive IDs 128-159
|
||||||
|
* - bf5: Check directive IDs 160 - 191
|
||||||
|
* - bf6: Check directive IDs 192 - 223
|
||||||
|
* - bf7: Check directive IDs 224 - 255
|
||||||
*
|
*
|
||||||
* See: https://en.wikipedia.org/wiki/Bloom_filter for more about bloom filters.
|
* See: https://en.wikipedia.org/wiki/Bloom_filter for more about bloom filters.
|
||||||
*/
|
*/
|
||||||
|
@ -45,9 +49,13 @@ export interface LInjector {
|
||||||
bf1: number;
|
bf1: number;
|
||||||
bf2: number;
|
bf2: number;
|
||||||
bf3: number;
|
bf3: number;
|
||||||
|
bf4: number;
|
||||||
|
bf5: number;
|
||||||
|
bf6: number;
|
||||||
|
bf7: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cbf0 - cbf3 properties determine whether a directive is available through a
|
* cbf0 - cbf7 properties determine whether a directive is available through a
|
||||||
* parent injector. They refer to the merged values of parent bloom filters. This
|
* parent injector. They refer to the merged values of parent bloom filters. This
|
||||||
* allows us to skip looking up the chain unless it's probable that directive exists
|
* allows us to skip looking up the chain unless it's probable that directive exists
|
||||||
* up the chain.
|
* up the chain.
|
||||||
|
@ -56,6 +64,11 @@ export interface LInjector {
|
||||||
cbf1: number;
|
cbf1: number;
|
||||||
cbf2: number;
|
cbf2: number;
|
||||||
cbf3: number;
|
cbf3: number;
|
||||||
|
cbf4: number;
|
||||||
|
cbf5: number;
|
||||||
|
cbf6: number;
|
||||||
|
cbf7: number;
|
||||||
|
|
||||||
injector: Injector|null;
|
injector: Injector|null;
|
||||||
|
|
||||||
/** Stores the TemplateRef so subsequent injections of the TemplateRef get the same instance. */
|
/** Stores the TemplateRef so subsequent injections of the TemplateRef get the same instance. */
|
||||||
|
|
|
@ -487,23 +487,42 @@ describe('di', () => {
|
||||||
di.bf1 = 0;
|
di.bf1 = 0;
|
||||||
di.bf2 = 0;
|
di.bf2 = 0;
|
||||||
di.bf3 = 0;
|
di.bf3 = 0;
|
||||||
|
di.bf4 = 0;
|
||||||
|
di.bf5 = 0;
|
||||||
|
di.bf6 = 0;
|
||||||
|
di.bf7 = 0;
|
||||||
|
di.bf3 = 0;
|
||||||
di.cbf0 = 0;
|
di.cbf0 = 0;
|
||||||
di.cbf1 = 0;
|
di.cbf1 = 0;
|
||||||
di.cbf2 = 0;
|
di.cbf2 = 0;
|
||||||
di.cbf3 = 0;
|
di.cbf3 = 0;
|
||||||
|
di.cbf4 = 0;
|
||||||
|
di.cbf5 = 0;
|
||||||
|
di.cbf6 = 0;
|
||||||
|
di.cbf7 = 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
function bloomState() { return [di.bf3, di.bf2, di.bf1, di.bf0]; }
|
function bloomState() {
|
||||||
|
return [di.bf7, di.bf6, di.bf5, di.bf4, di.bf3, di.bf2, di.bf1, di.bf0];
|
||||||
|
}
|
||||||
|
|
||||||
it('should add values', () => {
|
it('should add values', () => {
|
||||||
bloomAdd(di, { __NG_ELEMENT_ID__: 0 } as any);
|
bloomAdd(di, { __NG_ELEMENT_ID__: 0 } as any);
|
||||||
expect(bloomState()).toEqual([0, 0, 0, 1]);
|
expect(bloomState()).toEqual([0, 0, 0, 0, 0, 0, 0, 1]);
|
||||||
bloomAdd(di, { __NG_ELEMENT_ID__: 32 + 1 } as any);
|
bloomAdd(di, { __NG_ELEMENT_ID__: 32 + 1 } as any);
|
||||||
expect(bloomState()).toEqual([0, 0, 2, 1]);
|
expect(bloomState()).toEqual([0, 0, 0, 0, 0, 0, 2, 1]);
|
||||||
bloomAdd(di, { __NG_ELEMENT_ID__: 64 + 2 } as any);
|
bloomAdd(di, { __NG_ELEMENT_ID__: 64 + 2 } as any);
|
||||||
expect(bloomState()).toEqual([0, 4, 2, 1]);
|
expect(bloomState()).toEqual([0, 0, 0, 0, 0, 4, 2, 1]);
|
||||||
bloomAdd(di, { __NG_ELEMENT_ID__: 96 + 3 } as any);
|
bloomAdd(di, { __NG_ELEMENT_ID__: 96 + 3 } as any);
|
||||||
expect(bloomState()).toEqual([8, 4, 2, 1]);
|
expect(bloomState()).toEqual([0, 0, 0, 0, 8, 4, 2, 1]);
|
||||||
|
bloomAdd(di, { __NG_ELEMENT_ID__: 128 + 4 } as any);
|
||||||
|
expect(bloomState()).toEqual([0, 0, 0, 16, 8, 4, 2, 1]);
|
||||||
|
bloomAdd(di, { __NG_ELEMENT_ID__: 160 + 5 } as any);
|
||||||
|
expect(bloomState()).toEqual([0, 0, 32, 16, 8, 4, 2, 1]);
|
||||||
|
bloomAdd(di, { __NG_ELEMENT_ID__: 192 + 6 } as any);
|
||||||
|
expect(bloomState()).toEqual([0, 64, 32, 16, 8, 4, 2, 1]);
|
||||||
|
bloomAdd(di, { __NG_ELEMENT_ID__: 224 + 7 } as any);
|
||||||
|
expect(bloomState()).toEqual([128, 64, 32, 16, 8, 4, 2, 1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should query values', () => {
|
it('should query values', () => {
|
||||||
|
@ -511,12 +530,22 @@ describe('di', () => {
|
||||||
bloomAdd(di, { __NG_ELEMENT_ID__: 32 } as any);
|
bloomAdd(di, { __NG_ELEMENT_ID__: 32 } as any);
|
||||||
bloomAdd(di, { __NG_ELEMENT_ID__: 64 } as any);
|
bloomAdd(di, { __NG_ELEMENT_ID__: 64 } as any);
|
||||||
bloomAdd(di, { __NG_ELEMENT_ID__: 96 } as any);
|
bloomAdd(di, { __NG_ELEMENT_ID__: 96 } as any);
|
||||||
|
bloomAdd(di, { __NG_ELEMENT_ID__: 127 } as any);
|
||||||
|
bloomAdd(di, { __NG_ELEMENT_ID__: 161 } as any);
|
||||||
|
bloomAdd(di, { __NG_ELEMENT_ID__: 188 } as any);
|
||||||
|
bloomAdd(di, { __NG_ELEMENT_ID__: 223 } as any);
|
||||||
|
bloomAdd(di, { __NG_ELEMENT_ID__: 255 } as any);
|
||||||
|
|
||||||
expect(bloomFindPossibleInjector(di, 0)).toEqual(di);
|
expect(bloomFindPossibleInjector(di, 0)).toEqual(di);
|
||||||
expect(bloomFindPossibleInjector(di, 1)).toEqual(null);
|
expect(bloomFindPossibleInjector(di, 1)).toEqual(null);
|
||||||
expect(bloomFindPossibleInjector(di, 32)).toEqual(di);
|
expect(bloomFindPossibleInjector(di, 32)).toEqual(di);
|
||||||
expect(bloomFindPossibleInjector(di, 64)).toEqual(di);
|
expect(bloomFindPossibleInjector(di, 64)).toEqual(di);
|
||||||
expect(bloomFindPossibleInjector(di, 96)).toEqual(di);
|
expect(bloomFindPossibleInjector(di, 96)).toEqual(di);
|
||||||
|
expect(bloomFindPossibleInjector(di, 127)).toEqual(di);
|
||||||
|
expect(bloomFindPossibleInjector(di, 161)).toEqual(di);
|
||||||
|
expect(bloomFindPossibleInjector(di, 188)).toEqual(di);
|
||||||
|
expect(bloomFindPossibleInjector(di, 223)).toEqual(di);
|
||||||
|
expect(bloomFindPossibleInjector(di, 255)).toEqual(di);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue