1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-09-04 01:59:38 +02:00

Revisit realm & action bits

The important bit is now considered an action bit
so that there is no more a need for the `important`
property in the parser. The modify bit is now
considered a realm bit.

When the modify bit is set, the action bits become
available to be used to further narrow the realm.
This could be useful in the future if we want to
spread the population of modifier filters across
different buckets.
This commit is contained in:
Raymond Hill 2020-11-11 07:53:46 -05:00
parent 2f3d3d78ca
commit 0e851c035e
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2

View File

@ -36,34 +36,27 @@ const µb = µBlock;
// | | || |
// | | || |
// | | || |
// | | || +---- bit 0- 1: block=0, allow=1, modify=2
// | | |+------ bit 2: important
// | | || +---- bit 0- 1: block=0, allow=1, block important=2
// | | |+------ bit 2: modifier
// | | +------- bit 3- 4: party [0-3]
// | +--------- bit 5- 9: type [0-31]
// +-------------- bit 10-15: unused
const CategoryCount = 1 << 0xa; // shift left to first unused bit
const RealmBitsMask = 0b0000000111;
const ActionBitsMask = 0b0000000011;
const TypeBitsMask = 0b1111100000;
const TypeBitsOffset = 5;
const BlockAction = 0b00 << 0;
const AllowAction = 0b01 << 0;
const ModifyAction = 0b10 << 0;
// Note:
// It's possible to increase granularity of ModifyAction realm with
// sub-realms if it helps performance, but so far I found it's not
// needed, there is no meaningful gains to be had.
const Important = 1 << 2;
const BlockAction = 0b0000000000;
const AllowAction = 0b0000000001;
const Important = 0b0000000010;
const BlockImportant = BlockAction | Important;
const AnyParty = 0b00 << 3;
const FirstParty = 0b01 << 3;
const ThirdParty = 0b10 << 3;
const AllParties = 0b11 << 3;
const ModifyAction = 0b0000000100;
const AnyParty = 0b0000000000;
const FirstParty = 0b0000001000;
const ThirdParty = 0b0000010000;
const AllParties = 0b0000011000;
const typeNameToTypeValue = {
'no_type': 0 << TypeBitsOffset,
@ -1562,7 +1555,7 @@ const FilterModifier = class {
static compile(details) {
return [
FilterModifier.fid,
details.action | details.important,
details.action,
details.modifyType,
details.modifyValue
];
@ -1590,7 +1583,7 @@ const FilterModifierResult = class {
constructor(bits, th, iunit) {
this.iunit = iunit;
this.th = th;
this.bits = (bits & ~ActionBitsMask) | this.modifier.actionBits;
this.bits = (bits & ~RealmBitsMask) | this.modifier.actionBits;
}
get filter() {
@ -1632,8 +1625,7 @@ const FilterCollection = class {
}
unshift(iunit) {
const j = this.i;
this.i = filterSequenceAdd(iunit, j);
this.i = filterSequenceAdd(iunit, this.i);
}
shift(drop = false) {
@ -2610,7 +2602,6 @@ const FilterParser = class {
this.tokenBeg = 0;
this.typeBits = 0;
this.notTypes = 0;
this.important = 0;
this.firstWildcardPos = -1;
this.secondWildcardPos = -1;
this.firstCaretPos = -1;
@ -2718,7 +2709,8 @@ const FilterParser = class {
this.parseTypeOption(parser.OPTTokenGhide, not);
break;
case parser.OPTTokenImportant:
this.important = Important;
if ( this.action === AllowAction ) { return false; }
this.action = BlockImportant;
break;
// Used by Adguard:
// https://kb.adguard.com/en/general/how-to-create-your-own-ad-filters#empty-modifier
@ -3169,29 +3161,7 @@ FilterContainer.prototype.freeze = function() {
// readiness when no valid selfie is available.
this.optimizeTimerId = self.requestIdleCallback(( ) => {
this.optimizeTimerId = undefined;
for ( let bits = 0, n = this.categories.length; bits < n; bits++ ) {
const bucket = this.categories[bits];
if ( bucket === undefined ) { continue; }
for ( const [ th, iunit ] of bucket ) {
const f = filterUnits[iunit];
if ( f instanceof FilterBucket === false ) { continue; }
const optimizeBits =
(th === this.noTokenHash) ||
(bits & ActionBitsMask) === ModifyAction
? 0b10
: 0b01;
const g = f.optimize(optimizeBits);
if ( g !== undefined ) {
filterUnits[iunit] = g;
}
}
}
FilterHostnameDict.optimize();
bidiTrieOptimize();
// Be sure unused filters can be garbage collected.
for ( let i = filterUnitWritePtr, n = filterUnits.length; i < n; i++ ) {
filterUnits[i] = null;
}
this.optimize();
}, { timeout: 15000 });
log.info(`staticNetFilteringEngine.freeze() took ${Date.now()-t0} ms`);
@ -3199,6 +3169,33 @@ FilterContainer.prototype.freeze = function() {
/******************************************************************************/
FilterContainer.prototype.optimize = function() {
for ( let bits = 0, n = this.categories.length; bits < n; bits++ ) {
const bucket = this.categories[bits];
if ( bucket === undefined ) { continue; }
for ( const [ th, iunit ] of bucket ) {
const f = filterUnits[iunit];
if ( f instanceof FilterBucket === false ) { continue; }
const optimizeBits =
(th === this.noTokenHash) || (bits & ModifyAction) !== 0
? 0b10
: 0b01;
const g = f.optimize(optimizeBits);
if ( g !== undefined ) {
filterUnits[iunit] = g;
}
}
}
FilterHostnameDict.optimize();
bidiTrieOptimize();
// Be sure unused filters can be garbage collected.
for ( let i = filterUnitWritePtr, n = filterUnits.length; i < n; i++ ) {
filterUnits[i] = null;
}
};
/******************************************************************************/
FilterContainer.prototype.toSelfie = function(path) {
const categoriesToSelfie = ( ) => {
const selfie = [];
@ -3437,18 +3434,16 @@ FilterContainer.prototype.compile = function(parser, writer) {
if ( parsed.modifyType !== undefined ) {
if (
parsed.modifyType === parser.OPTTokenRedirect &&
(parsed.action & ActionBitsMask) !== AllowAction
parsed.action !== AllowAction
) {
this.compileToAtomicFilter(
parsed,
FilterCompositeAll.compile(units),
writer
);
const fdata = units.length === 1
? units[0]
: FilterCompositeAll.compile(units);
this.compileToAtomicFilter(parsed, fdata, writer);
parsed.modifyType = parser.OPTTokenRedirectRule;
}
units.unshift(FilterModifier.compile(parsed));
parsed.action = ModifyAction;
parsed.important = 0;
}
const fdata = units.length === 1
@ -3470,7 +3465,7 @@ FilterContainer.prototype.compileToAtomicFilter = function(
// 1 = network filters: bad filters
writer.select(parsed.badFilter ? 1 : 0);
const catBits = parsed.action | parsed.important | parsed.party;
const catBits = parsed.action | parsed.party;
let typeBits = parsed.typeBits;
// Typeless
@ -3613,7 +3608,7 @@ FilterContainer.prototype.matchAndFetchModifiers = function(
// occurrences.
if ( results.length === 1 ) {
const result = results[0];
if ( (result.bits & ActionBitsMask) === AllowAction ) { return; }
if ( (result.bits & AllowAction) !== 0 ) { return; }
return [ result ];
}
@ -3899,9 +3894,7 @@ FilterContainer.prototype.redirectRequest = function(fctxt) {
// A single directive should be the next most common occurrence.
if ( directives.length === 1 ) {
const directive = directives[0];
if ( (directive.bits & ActionBitsMask) === AllowAction ) {
return directive;
}
if ( (directive.bits & AllowAction) !== 0 ) { return directive; }
const modifier = directive.modifier;
if ( modifier.cache === undefined ) {
modifier.cache = this.parseRedirectRequestValue(modifier.value);
@ -3917,7 +3910,7 @@ FilterContainer.prototype.redirectRequest = function(fctxt) {
let winningPriority = 0;
for ( const directive of directives ) {
const modifier = directive.modifier;
const isException = (directive.bits & ActionBitsMask) === AllowAction;
const isException = (directive.bits & AllowAction) !== 0;
if ( isException && modifier.value === '' ) {
winningDirective = directive;
break;
@ -3934,7 +3927,7 @@ FilterContainer.prototype.redirectRequest = function(fctxt) {
}
}
if ( winningDirective === undefined ) { return; }
if ( (winningDirective.bits & ActionBitsMask) !== AllowAction ) {
if ( (winningDirective.bits & AllowAction) === 0 ) {
fctxt.redirectURL = µb.redirectEngine.tokenToURL(
fctxt,
winningDirective.modifier.cache.token
@ -3966,7 +3959,7 @@ FilterContainer.prototype.filterQuery = function(fctxt) {
const out = [];
for ( const directive of directives ) {
const modifier = directive.modifier;
const isException = (directive.bits & ActionBitsMask) === AllowAction;
const isException = (directive.bits & AllowAction) !== 0;
if ( isException && modifier.value === '' ) {
out.push(directive);
break;
@ -4028,14 +4021,14 @@ FilterContainer.prototype.toLogData = function() {
logData.tokenHash = this.$tokenHash;
logData.result = this.$filterUnit === 0
? 0
: ((this.$catBits & AllowAction) !== 0 ? 2 : 1);
: ((this.$catBits & AllowAction) === 0 ? 1 : 2);
return logData;
};
/******************************************************************************/
FilterContainer.prototype.isBlockImportant = function() {
return (this.$catBits & BlockImportant) === BlockImportant;
return (this.$catBits & ActionBitsMask) === BlockImportant;
};
/******************************************************************************/