mirror of https://github.com/gorhill/uBlock.git
fix badfilter option; performance work
- badfilter option was no longer working following last refactoring changes. - performance work: - reduce duplication of large strings. - new lighter FilterBucket to use when only 2 filters: FilterPair.
This commit is contained in:
parent
1c685c86a7
commit
aae97b8535
|
@ -121,8 +121,8 @@ var µBlock = (function() { // jshint ignore:line
|
||||||
|
|
||||||
// read-only
|
// read-only
|
||||||
systemSettings: {
|
systemSettings: {
|
||||||
compiledMagic: 'ohszqbtqggmp',
|
compiledMagic: 'pwvcdyqfkuek',
|
||||||
selfieMagic: 'ohszqbtqggmp'
|
selfieMagic: 'pwvcdyqfkuek'
|
||||||
},
|
},
|
||||||
|
|
||||||
restoreBackupSettings: {
|
restoreBackupSettings: {
|
||||||
|
|
|
@ -259,6 +259,23 @@ var isHnAnchored = function(url, matchStart) {
|
||||||
|
|
||||||
var reURLPostHostnameAnchors = /[\/?#]/;
|
var reURLPostHostnameAnchors = /[\/?#]/;
|
||||||
|
|
||||||
|
var arrayStrictEquals = function(a, b) {
|
||||||
|
var n = a.length;
|
||||||
|
if ( n !== b.length ) { return false; }
|
||||||
|
var isArray, x, y;
|
||||||
|
for ( var i = 0; i < n; i++ ) {
|
||||||
|
x = a[i]; y = b[i];
|
||||||
|
isArray = Array.isArray(x);
|
||||||
|
if ( isArray !== Array.isArray(y) ) { return false; }
|
||||||
|
if ( isArray === true ) {
|
||||||
|
if ( arrayStrictEquals(x, y) === false ) { return false; }
|
||||||
|
} else {
|
||||||
|
if ( x !== y ) { return false; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
|
||||||
Each filter class will register itself in the map. A filter class
|
Each filter class will register itself in the map. A filter class
|
||||||
|
@ -804,7 +821,9 @@ FilterOriginMiss.prototype = Object.create(FilterOrigin.prototype, {
|
||||||
|
|
||||||
var FilterOriginHitSet = function(domainOpt) {
|
var FilterOriginHitSet = function(domainOpt) {
|
||||||
FilterOrigin.call(this);
|
FilterOrigin.call(this);
|
||||||
this.domainOpt = domainOpt;
|
this.domainOpt = domainOpt.length < 128
|
||||||
|
? domainOpt
|
||||||
|
: µb.stringDeduplicater.lookup(domainOpt);
|
||||||
};
|
};
|
||||||
|
|
||||||
FilterOriginHitSet.prototype = Object.create(FilterOrigin.prototype, {
|
FilterOriginHitSet.prototype = Object.create(FilterOrigin.prototype, {
|
||||||
|
@ -834,7 +853,9 @@ FilterOriginHitSet.prototype = Object.create(FilterOrigin.prototype, {
|
||||||
|
|
||||||
var FilterOriginMissSet = function(domainOpt) {
|
var FilterOriginMissSet = function(domainOpt) {
|
||||||
FilterOrigin.call(this);
|
FilterOrigin.call(this);
|
||||||
this.domainOpt = domainOpt;
|
this.domainOpt = domainOpt.length < 128
|
||||||
|
? domainOpt
|
||||||
|
: µb.stringDeduplicater.lookup(domainOpt);
|
||||||
};
|
};
|
||||||
|
|
||||||
FilterOriginMissSet.prototype = Object.create(FilterOrigin.prototype, {
|
FilterOriginMissSet.prototype = Object.create(FilterOrigin.prototype, {
|
||||||
|
@ -864,7 +885,9 @@ FilterOriginMissSet.prototype = Object.create(FilterOrigin.prototype, {
|
||||||
|
|
||||||
var FilterOriginMixedSet = function(domainOpt) {
|
var FilterOriginMixedSet = function(domainOpt) {
|
||||||
FilterOrigin.call(this);
|
FilterOrigin.call(this);
|
||||||
this.domainOpt = domainOpt;
|
this.domainOpt = domainOpt.length < 128
|
||||||
|
? domainOpt
|
||||||
|
: µb.stringDeduplicater.lookup(domainOpt);
|
||||||
};
|
};
|
||||||
|
|
||||||
FilterOriginMixedSet.prototype = Object.create(FilterOrigin.prototype, {
|
FilterOriginMixedSet.prototype = Object.create(FilterOrigin.prototype, {
|
||||||
|
@ -1115,15 +1138,91 @@ registerFilterClass(FilterHostnameDict);
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var FilterBucket = function(a, b) {
|
var FilterPair = function(a, b) {
|
||||||
|
this.f1 = a;
|
||||||
|
this.f2 = b;
|
||||||
|
this.f = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.defineProperty(FilterPair.prototype, 'size', {
|
||||||
|
get: function() {
|
||||||
|
if ( this.f1 === undefined && this.f2 === undefined ) { return 0; }
|
||||||
|
if ( this.f1 === undefined || this.f2 === undefined ) { return 1; }
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
FilterPair.prototype.remove = function(fdata) {
|
||||||
|
if ( arrayStrictEquals(this.f2.compile(), fdata) === true ) {
|
||||||
|
this.f2 = undefined;
|
||||||
|
}
|
||||||
|
if ( arrayStrictEquals(this.f1.compile(), fdata) === true ) {
|
||||||
|
this.f1 = this.f2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FilterPair.prototype.match = function(url, tokenBeg) {
|
||||||
|
if ( this.f1.match(url, tokenBeg) === true ) {
|
||||||
|
this.f = this.f1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ( this.f2.match(url, tokenBeg) === true ) {
|
||||||
|
this.f = this.f2;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
FilterPair.prototype.logData = function() {
|
||||||
|
return this.f.logData();
|
||||||
|
};
|
||||||
|
|
||||||
|
FilterPair.prototype.compile = function() {
|
||||||
|
return [ this.fid, this.f1.compile(), this.f2.compile() ];
|
||||||
|
};
|
||||||
|
|
||||||
|
FilterPair.prototype.upgrade = function(a) {
|
||||||
|
var bucket = new FilterBucket(this.f1, this.f2, a);
|
||||||
|
this.f1 = this.f2 = this.f = null;
|
||||||
|
FilterPair.available = this;
|
||||||
|
return bucket;
|
||||||
|
};
|
||||||
|
|
||||||
|
FilterPair.load = function(args) {
|
||||||
|
var f1 = filterFromCompiledData(args[1]),
|
||||||
|
f2 = filterFromCompiledData(args[2]),
|
||||||
|
pair = FilterPair.available;
|
||||||
|
if ( pair === null ) {
|
||||||
|
return new FilterPair(f1, f2);
|
||||||
|
}
|
||||||
|
FilterPair.available = null;
|
||||||
|
pair.f1 = f1;
|
||||||
|
pair.f2 = f2;
|
||||||
|
return pair;
|
||||||
|
};
|
||||||
|
|
||||||
|
FilterPair.available = null;
|
||||||
|
|
||||||
|
registerFilterClass(FilterPair);
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var FilterBucket = function(a, b, c) {
|
||||||
this.filters = [];
|
this.filters = [];
|
||||||
this.f = null;
|
this.f = null;
|
||||||
if ( a !== undefined ) {
|
if ( a !== undefined ) {
|
||||||
this.filters[0] = a;
|
this.filters[0] = a;
|
||||||
this.filters[1] = b;
|
this.filters[1] = b;
|
||||||
|
this.filters[2] = c;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Object.defineProperty(FilterBucket.prototype, 'size', {
|
||||||
|
get: function() {
|
||||||
|
return this.filters.length;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
FilterBucket.prototype.promoted = 0;
|
FilterBucket.prototype.promoted = 0;
|
||||||
|
|
||||||
FilterBucket.prototype.add = function(fdata) {
|
FilterBucket.prototype.add = function(fdata) {
|
||||||
|
@ -1135,7 +1234,7 @@ FilterBucket.prototype.remove = function(fdata) {
|
||||||
filter;
|
filter;
|
||||||
while ( i-- ) {
|
while ( i-- ) {
|
||||||
filter = this.filters[i];
|
filter = this.filters[i];
|
||||||
if ( filter.compile() === fdata ) {
|
if ( arrayStrictEquals(filter.compile(), fdata) === true ) {
|
||||||
this.filters.splice(i, 1);
|
this.filters.splice(i, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1184,13 +1283,13 @@ FilterBucket.prototype.compile = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
FilterBucket.load = function(args) {
|
FilterBucket.load = function(args) {
|
||||||
var f = new FilterBucket(),
|
var bucket = new FilterBucket(),
|
||||||
compiledFilters = args[1],
|
compiledFilters = args[1],
|
||||||
filters = f.filters;
|
filters = bucket.filters;
|
||||||
for ( var i = 0, n = compiledFilters.length; i < n; i++ ) {
|
for ( var i = 0, n = compiledFilters.length; i < n; i++ ) {
|
||||||
filters[i] = filterFromCompiledData(compiledFilters[i]);
|
filters[i] = filterFromCompiledData(compiledFilters[i]);
|
||||||
}
|
}
|
||||||
return f;
|
return bucket;
|
||||||
};
|
};
|
||||||
|
|
||||||
registerFilterClass(FilterBucket);
|
registerFilterClass(FilterBucket);
|
||||||
|
@ -2063,6 +2162,7 @@ FilterContainer.prototype.compileToAtomicFilter = function(fdata, parsed, writer
|
||||||
|
|
||||||
FilterContainer.prototype.fromCompiledContent = function(reader) {
|
FilterContainer.prototype.fromCompiledContent = function(reader) {
|
||||||
var badFilterBit = BadFilter,
|
var badFilterBit = BadFilter,
|
||||||
|
filterPairId = FilterPair.fid,
|
||||||
filterBucketId = FilterBucket.fid,
|
filterBucketId = FilterBucket.fid,
|
||||||
filterDataHolderId = FilterDataHolder.fid,
|
filterDataHolderId = FilterDataHolder.fid,
|
||||||
args, bits, bucket, entry,
|
args, bits, bucket, entry,
|
||||||
|
@ -2141,14 +2241,27 @@ FilterContainer.prototype.fromCompiledContent = function(reader) {
|
||||||
entry.add(fdata);
|
entry.add(fdata);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
bucket.set(tokenHash, new FilterBucket(entry, filterFromCompiledData(fdata)));
|
if ( entry.fid === filterPairId ) {
|
||||||
|
bucket.set(
|
||||||
|
tokenHash,
|
||||||
|
entry.upgrade(filterFromCompiledData(fdata))
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bucket.set(
|
||||||
|
tokenHash,
|
||||||
|
new FilterPair(entry, filterFromCompiledData(fdata))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
FilterContainer.prototype.removeBadFilters = function() {
|
FilterContainer.prototype.removeBadFilters = function() {
|
||||||
var bits, tokenHash, fdata, bucket, entry;
|
var filterPairId = FilterPair.fid,
|
||||||
|
filterBucketId = FilterBucket.fid,
|
||||||
|
filterHostnameDictId = FilterHostnameDict.fid,
|
||||||
|
bits, tokenHash, fdata, bucket, entry;
|
||||||
for ( var args of this.badFilters ) {
|
for ( var args of this.badFilters ) {
|
||||||
bits = args[0] & ~BadFilter;
|
bits = args[0] & ~BadFilter;
|
||||||
bucket = this.categories.get(bits);
|
bucket = this.categories.get(bits);
|
||||||
|
@ -2157,14 +2270,24 @@ FilterContainer.prototype.removeBadFilters = function() {
|
||||||
entry = bucket.get(tokenHash);
|
entry = bucket.get(tokenHash);
|
||||||
if ( entry === undefined ) { continue; }
|
if ( entry === undefined ) { continue; }
|
||||||
fdata = args[2];
|
fdata = args[2];
|
||||||
if ( entry instanceof FilterBucket ) {
|
if ( entry.fid === filterPairId ) {
|
||||||
entry.remove(fdata);
|
entry.remove(fdata);
|
||||||
if ( entry.filters.length === 1 ) {
|
if ( entry.size === 1 ) {
|
||||||
bucket.set(tokenHash, entry.filters[0]);
|
bucket.set(tokenHash, entry.f1);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ( entry instanceof FilterHostnameDict ) {
|
if ( entry.fid === filterBucketId ) {
|
||||||
|
entry.remove(fdata);
|
||||||
|
if ( entry.size === 2 ) {
|
||||||
|
bucket.set(
|
||||||
|
tokenHash,
|
||||||
|
new FilterPair(entry.filters[0], entry.filters[1])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( entry.fid === filterHostnameDictId ) {
|
||||||
entry.remove(fdata);
|
entry.remove(fdata);
|
||||||
if ( entry.size === 0 ) {
|
if ( entry.size === 0 ) {
|
||||||
bucket.delete(tokenHash);
|
bucket.delete(tokenHash);
|
||||||
|
@ -2174,7 +2297,7 @@ FilterContainer.prototype.removeBadFilters = function() {
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ( entry.compile() === fdata ) {
|
if ( arrayStrictEquals(entry.compile(), fdata) === true ) {
|
||||||
bucket.delete(tokenHash);
|
bucket.delete(tokenHash);
|
||||||
if ( bucket.size === 0 ) {
|
if ( bucket.size === 0 ) {
|
||||||
this.categories.delete(bits);
|
this.categories.delete(bits);
|
||||||
|
|
|
@ -280,6 +280,42 @@
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// I want this helper to be self-maintained, callers must not worry about
|
||||||
|
// this helper cleaning after itself by asking them to reset it when it is no
|
||||||
|
// longer needed. A timer will be used for self-garbage-collect.
|
||||||
|
// Cleaning up 10s after last hit sounds reasonable.
|
||||||
|
|
||||||
|
µBlock.stringDeduplicater = {
|
||||||
|
strings: new Map(),
|
||||||
|
timer: undefined,
|
||||||
|
last: 0,
|
||||||
|
|
||||||
|
lookup: function(s) {
|
||||||
|
var t = this.strings.get(s);
|
||||||
|
if ( t === undefined ) {
|
||||||
|
t = this.strings.set(s, s).get(s);
|
||||||
|
if ( this.timer === undefined ) { this.cleanupAsync(); }
|
||||||
|
}
|
||||||
|
this.last = Date.now();
|
||||||
|
return t;
|
||||||
|
},
|
||||||
|
|
||||||
|
cleanupAsync: function() {
|
||||||
|
this.timer = vAPI.setTimeout(this.cleanup.bind(this), 10000);
|
||||||
|
},
|
||||||
|
|
||||||
|
cleanup: function() {
|
||||||
|
if ( (Date.now() - this.last) < 10000 ) {
|
||||||
|
this.timer = vAPI.setTimeout(this.cleanup.bind(this), 10000);
|
||||||
|
} else {
|
||||||
|
this.timer = undefined;
|
||||||
|
this.strings.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
µBlock.mapToArray = typeof Array.from === 'function'
|
µBlock.mapToArray = typeof Array.from === 'function'
|
||||||
? Array.from
|
? Array.from
|
||||||
: function(map) {
|
: function(map) {
|
||||||
|
|
Loading…
Reference in New Issue