1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-09-15 07:22:28 +02:00

Add filter instance deduplicater in static net filtering engine

Provide a way to optionally deduplicate filter
instances, useful for filter instances with:

- high likelihood of duplication; and
- non-trivial memory footprint per instance
  - For examples, filter instances to implement
    `domain=`, `denyallow=`, `csp=`.

Cursory tests show this helps further reduce
uBO's memory footprint.
This commit is contained in:
Raymond Hill 2020-03-18 09:06:33 -04:00
parent 8b69af0dda
commit ab629b9e10
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2

View File

@ -287,10 +287,6 @@ const isSeparatorChar = c => (charClassMap[c] & CHAR_CLASS_SEPARATOR) !== 0;
/******************************************************************************/ /******************************************************************************/
// TODO: Unify [ string instance, string usage instance ] pairs
/******************************************************************************/
let filterUnits = [ null ]; let filterUnits = [ null ];
let filterSequences = new Uint32Array(131072); let filterSequences = new Uint32Array(131072);
@ -378,6 +374,29 @@ const filterFromSelfie = function(args) {
/******************************************************************************/ /******************************************************************************/
// Optional manager to be used to reuse filters instances. This is useful
// when deduplicating instances is worth it. For examples, filter instances
// which have a high likelihood of larger than average memory usage per
// instance.
const filterInstanceManager = {
unitFromArgs: function(args, createFn) {
const key = JSON.stringify(args);
let iunit = this.argsToUnit.get(key);
if ( iunit === undefined ) {
iunit = createFn(args);
this.argsToUnit.set(key, iunit);
}
return iunit;
},
reset: function() {
this.argsToUnit.clear();
},
argsToUnit: new Map(),
};
/******************************************************************************/
const filterPattern = { const filterPattern = {
compile: function(parsed, units) { compile: function(parsed, units) {
if ( parsed.isRegex ) { if ( parsed.isRegex ) {
@ -838,8 +857,10 @@ const FilterPatternGeneric = class {
} }
static unitFromCompiled(args) { static unitFromCompiled(args) {
const f = new FilterPatternGeneric(args[1], args[2]); return filterInstanceManager.unitFromArgs(args, args => {
return filterUnits.push(f) - 1; const f = new FilterPatternGeneric(args[1], args[2]);
return filterUnits.push(f) - 1;
});
} }
static fromSelfie(args) { static fromSelfie(args) {
@ -1139,8 +1160,10 @@ const FilterRegex = class {
} }
static unitFromCompiled(args) { static unitFromCompiled(args) {
const f = new FilterRegex(args[1]); return filterInstanceManager.unitFromArgs(args, args => {
return filterUnits.push(f) - 1; const f = new FilterRegex(args[1]);
return filterUnits.push(f) - 1;
});
} }
static fromSelfie(args) { static fromSelfie(args) {
@ -1163,8 +1186,6 @@ registerFilterClass(FilterRegex);
const filterOrigin = new (class { const filterOrigin = new (class {
constructor() { constructor() {
this.trieContainer = new µb.HNTrieContainer(); this.trieContainer = new µb.HNTrieContainer();
this.strToUnitMap = new Map();
this.gcTimer = undefined;
} }
compile(details, prepend, units) { compile(details, prepend, units) {
@ -1219,23 +1240,6 @@ const filterOrigin = new (class {
} }
} }
unitFromCompiled(ctor, s) {
let iunit = this.strToUnitMap.get(s);
if ( iunit !== undefined ) { return iunit; }
const f = new ctor(s);
iunit = filterUnits.push(f) - 1;
this.strToUnitMap.set(s, iunit);
if ( this.gcTimer !== undefined ) { return iunit; }
this.gcTimer = self.setTimeout(
( ) => {
this.gcTimer = undefined;
this.strToUnitMap.clear();
},
5000
);
return iunit;
}
prime() { prime() {
this.trieContainer.reset( this.trieContainer.reset(
vAPI.localStorage.getItem('SNFE.filterOrigin.trieDetails') vAPI.localStorage.getItem('SNFE.filterOrigin.trieDetails')
@ -1244,7 +1248,6 @@ const filterOrigin = new (class {
reset() { reset() {
this.trieContainer.reset(); this.trieContainer.reset();
this.strToUnitMap.clear();
} }
optimize() { optimize() {
@ -1293,7 +1296,8 @@ const FilterOriginHit = class {
} }
static unitFromCompiled(args) { static unitFromCompiled(args) {
return filterOrigin.unitFromCompiled(FilterOriginHit, args[1]); const f = new FilterOriginHit(args[1]);
return filterUnits.push(f) - 1;
} }
static fromSelfie(args) { static fromSelfie(args) {
@ -1337,7 +1341,8 @@ const FilterOriginMiss = class {
} }
static unitFromCompiled(args) { static unitFromCompiled(args) {
return filterOrigin.unitFromCompiled(FilterOriginMiss, args[1]); const f = new FilterOriginMiss(args[1]);
return filterUnits.push(f) - 1;
} }
static fromSelfie(args) { static fromSelfie(args) {
@ -1385,7 +1390,10 @@ const FilterOriginHitSet = class {
} }
static unitFromCompiled(args) { static unitFromCompiled(args) {
return filterOrigin.unitFromCompiled(FilterOriginHitSet, args[1]); return filterInstanceManager.unitFromArgs(args, args => {
const f = new FilterOriginHitSet(args[1]);
return filterUnits.push(f) - 1;
});
} }
static fromSelfie(args) { static fromSelfie(args) {
@ -1433,7 +1441,10 @@ const FilterOriginMissSet = class {
} }
static unitFromCompiled(args) { static unitFromCompiled(args) {
return filterOrigin.unitFromCompiled(FilterOriginMissSet, args[1]); return filterInstanceManager.unitFromArgs(args, args => {
const f = new FilterOriginMissSet(args[1]);
return filterUnits.push(f) - 1;
});
} }
static fromSelfie(args) { static fromSelfie(args) {
@ -1483,8 +1494,10 @@ const FilterDataHolder = class {
} }
static unitFromCompiled(args) { static unitFromCompiled(args) {
const f = new FilterDataHolder(args[1], args[2]); return filterInstanceManager.unitFromArgs(args, args => {
return filterUnits.push(f) - 1; const f = new FilterDataHolder(args[1], args[2]);
return filterUnits.push(f) - 1;
});
} }
static fromSelfie(args) { static fromSelfie(args) {
@ -1742,12 +1755,14 @@ const FilterDenyAllow = class {
} }
static unitFromCompiled(args) { static unitFromCompiled(args) {
const f = new FilterDenyAllow(args[1]); return filterInstanceManager.unitFromArgs(args, args => {
for ( const hn of args[1].split('|') ) { const f = new FilterDenyAllow(args[1]);
if ( hn === '' ) { continue; } for ( const hn of args[1].split('|') ) {
f.hndict.add(hn); if ( hn === '' ) { continue; }
} f.hndict.add(hn);
return filterUnits.push(f) - 1; }
return filterUnits.push(f) - 1;
});
} }
static fromSelfie(args) { static fromSelfie(args) {
@ -2739,6 +2754,7 @@ FilterContainer.prototype.reset = function() {
FilterHostnameDict.reset(); FilterHostnameDict.reset();
filterOrigin.reset(); filterOrigin.reset();
bidiTrie.reset(); bidiTrie.reset();
filterInstanceManager.reset();
filterUnits = filterUnits.slice(0, FILTER_UNITS_MIN); filterUnits = filterUnits.slice(0, FILTER_UNITS_MIN);
filterSequenceWritePtr = FILTER_SEQUENCES_MIN; filterSequenceWritePtr = FILTER_SEQUENCES_MIN;
@ -2861,6 +2877,7 @@ FilterContainer.prototype.freeze = function() {
FilterHostnameDict.optimize(); FilterHostnameDict.optimize();
bidiTrieOptimize(); bidiTrieOptimize();
filterInstanceManager.reset();
log.info(`staticNetFilteringEngine.freeze() took ${Date.now()-t0} ms`); log.info(`staticNetFilteringEngine.freeze() took ${Date.now()-t0} ms`);
}; };