diff --git a/src/js/background.js b/src/js/background.js index be8c6e48a..5a7bc2e24 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -175,7 +175,7 @@ const µBlock = { // jshint ignore:line // Read-only systemSettings: { compiledMagic: 44, // Increase when compiled format changes - selfieMagic: 44, // Increase when selfie format changes + selfieMagic: 45, // Increase when selfie format changes }, // https://github.com/uBlockOrigin/uBlock-issues/issues/759#issuecomment-546654501 diff --git a/src/js/hntrie.js b/src/js/hntrie.js index e83dfe2d9..d4953310c 100644 --- a/src/js/hntrie.js +++ b/src/js/hntrie.js @@ -223,7 +223,7 @@ class HNTrieContainer { return -1; } - createTrie(hostnames = undefined) { + createTrie() { // grow buffer if needed if ( (this.buf32[CHAR0_SLOT] - this.buf32[TRIE1_SLOT]) < 12 ) { this.growBuf(12, 0); @@ -233,12 +233,41 @@ class HNTrieContainer { this.buf32[iroot+0] = 0; this.buf32[iroot+1] = 0; this.buf32[iroot+2] = 0; - if ( hostnames !== undefined ) { - for ( const hn of hostnames ) { - this.setNeedle(hn).add(iroot); + return iroot; + } + + createTrieFromIterable(hostnames) { + const itrie = this.createTrie(); + for ( const hn of hostnames ) { + if ( hn === '' ) { continue; } + this.setNeedle(hn).add(itrie); + } + return itrie; + } + + createTrieFromStoredDomainOpt(i, n) { + const itrie = this.createTrie(); + const jend = i + n; + let j = i, offset = 0, k = 0, c = 0; + while ( j !== jend ) { + offset = this.buf32[CHAR0_SLOT]; // Important + k = 0; + for (;;) { + if ( j === jend ) { break; } + c = this.buf[offset+j]; + j += 1; + if ( c === 0x7C /* '|' */ ) { break; } + if ( k === 255 ) { continue; } + this.buf[k] = c; + k += 1; + } + if ( k !== 0 ) { + this.buf[255] = k; + this.add(itrie); } } - return iroot; + this.needle = ''; // Important + return itrie; } dumpTrie(iroot) { @@ -304,6 +333,12 @@ class HNTrieContainer { }; } + // TODO: + // Rework code to add from a string already present in the character + // buffer, i.e. not having to go through setNeedle() when adding a new + // hostname to a trie. This will require much work though, and probably + // changing the order in which string segments are stored in the + // character buffer. addJS(iroot) { let lhnchar = this.buf[255]; if ( lhnchar === 0 ) { return 0; } @@ -488,6 +523,31 @@ class HNTrieContainer { return textDecoder.decode(this.buf.subarray(offset, offset + n)); } + storeDomainOpt(s) { + let n = s.length; + if ( n === this.lastStoredLen && s === this.lastStored ) { + return this.lastStoredIndex; + } + this.lastStored = s; + this.lastStoredLen = n; + if ( (this.buf.length - this.buf32[CHAR1_SLOT]) < n ) { + this.growBuf(0, n); + } + const offset = this.buf32[CHAR1_SLOT]; + this.buf32[CHAR1_SLOT] = offset + n; + const buf8 = this.buf; + for ( let i = 0; i < n; i++ ) { + buf8[offset+i] = s.charCodeAt(i); + } + return (this.lastStoredIndex = offset - this.buf32[CHAR0_SLOT]); + } + + extractDomainOpt(i, n) { + const textDecoder = new TextDecoder(); + const offset = this.buf32[CHAR0_SLOT] + i; + return textDecoder.decode(this.buf.subarray(offset, offset + n)); + } + matchesHostname(hn, i, n) { this.setNeedle(hn); const buf8 = this.buf; diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 2835dac23..adba8e6a5 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -1200,10 +1200,6 @@ class DomainOptIterator { // A helper instance to reuse throughout const domainOptIterator = new DomainOptIterator(''); -const domainOptNormalizer = domainOpt => { - return domainOpt.split('|').sort().join('|'); -}; - /******************************************************************************/ // The optimal class is picked according to the content of the `domain=` @@ -1367,15 +1363,10 @@ registerFilterClass(FilterOriginMiss); /******************************************************************************/ const FilterOriginHitSet = class { - // The `domainOpt` value may be in either the allocated refs or the trie, - // never in both at the same time. static getDomainOpt(idata) { - const itrie = filterData[idata+2]; - if ( itrie === 0 ) { - return filterRefs[filterData[idata+4]].domainOpt; - } - return domainOptNormalizer( - Array.from(origHNTrieContainer.trieIterator(itrie)).join('|') + return origHNTrieContainer.extractDomainOpt( + filterData[idata+1], + filterData[idata+2] ); } @@ -1384,13 +1375,13 @@ const FilterOriginHitSet = class { } static match(idata) { - const refs = filterRefs[filterData[idata+4]]; + const refs = filterRefs[filterData[idata+6]]; if ( $docHostname === refs.$last ) { - return filterData[idata+3] !== -1; + return filterData[idata+5] !== -1; } refs.$last = $docHostname; - const which = filterData[idata+1]; - const itrie = filterData[idata+2] || this.toTrie(idata); + const which = filterData[idata+3]; + const itrie = filterData[idata+4] || this.toTrie(idata); let lastResult = -1; if ( (which & 0b01) !== 0 ) { lastResult = origHNTrieContainer @@ -1402,16 +1393,19 @@ const FilterOriginHitSet = class { .setNeedle($docEntity.compute()) .matches(itrie); } - return (filterData[idata+3] = lastResult) !== -1; + return (filterData[idata+5] = lastResult) !== -1; } static create(domainOpt, which = 0b11) { - const idata = filterDataAllocLen(5); + const idata = filterDataAllocLen(7); filterData[idata+0] = FilterOriginHitSet.fid; - filterData[idata+1] = which; - filterData[idata+2] = 0; // itrie - filterData[idata+3] = -1; // $lastResult - filterData[idata+4] = filterRefAdd({ domainOpt, $last: '' }); + filterData[idata+1] = origHNTrieContainer.storeDomainOpt(domainOpt); + filterData[idata+2] = domainOpt.length; + filterData[idata+3] = which; + filterData[idata+4] = 0; // itrie + filterData[idata+5] = -1; // $lastResult + filterData[idata+6] = filterRefAdd({ $last: '' }); + this.toTrie(idata); return idata; } @@ -1424,28 +1418,26 @@ const FilterOriginHitSet = class { } static fromCompiled(args) { - const idata = filterDataAllocLen(5); + const idata = filterDataAllocLen(7); filterData[idata+0] = args[0]; // fid - filterData[idata+1] = args[2]; // which - filterData[idata+2] = 0; // itrie - filterData[idata+3] = -1; // $lastResult - filterData[idata+4] = filterRefAdd({ domainOpt: args[1], $last: '' }); + filterData[idata+1] = origHNTrieContainer.storeDomainOpt(args[1]); + filterData[idata+2] = args[1].length; + filterData[idata+3] = args[2]; // which + filterData[idata+4] = 0; // itrie + filterData[idata+5] = -1; // $lastResult + filterData[idata+6] = filterRefAdd({ $last: '' }); return idata; } static toTrie(idata) { - const refs = filterRefs[filterData[idata+4]]; - const itrie = filterData[idata+2] = origHNTrieContainer.createTrie( - domainOptIterator.reset(refs.domainOpt) - ); - refs.domainOpt = ''; + const itrie = filterData[idata+4] = + origHNTrieContainer.createTrieFromStoredDomainOpt( + filterData[idata+1], + filterData[idata+2] + ); return itrie; } - static getTrie(idata) { - return filterData[idata+2]; - } - static keyFromArgs(args) { return args[1]; } @@ -1455,7 +1447,7 @@ const FilterOriginHitSet = class { } static dumpInfo(idata) { - return `0b${filterData[idata+1].toString(2)} ${this.getDomainOpt(idata)}`; + return `0b${filterData[idata+3].toString(2)} ${this.getDomainOpt(idata)}`; } }; @@ -1863,7 +1855,7 @@ const FilterHostnameDict = class { const itrie = filterData[idata+1]; if ( itrie !== 0 ) { return itrie; } const hostnames = filterRefs[filterData[idata+3]]; - filterData[idata+1] = destHNTrieContainer.createTrie(hostnames); + filterData[idata+1] = destHNTrieContainer.createTrieFromIterable(hostnames); filterRefs[filterData[idata+3]] = null; return filterData[idata+1]; } @@ -1908,11 +1900,9 @@ const FilterDenyAllow = class { } static fromCompiled(args) { - const itrie = destHNTrieContainer.createTrie(); - for ( const hn of domainOptIterator.reset(args[1]) ) { - if ( hn === '' ) { continue; } - destHNTrieContainer.setNeedle(hn).add(itrie); - } + const itrie = destHNTrieContainer.createTrieFromIterable( + domainOptIterator.reset(args[1]) + ); const idata = filterDataAllocLen(3); filterData[idata+0] = args[0]; // fid filterData[idata+1] = itrie; // itrie @@ -3503,7 +3493,7 @@ FilterCompiler.prototype.FILTER_UNSUPPORTED = 2; const FilterContainer = function() { this.compilerVersion = '6'; - this.selfieVersion = '6'; + this.selfieVersion = '7'; this.MAX_TOKEN_LENGTH = MAX_TOKEN_LENGTH; this.optimizeTaskId = undefined; @@ -4579,7 +4569,7 @@ FilterContainer.prototype.dump = function() { const out = []; - const toOutput = (depth, line) => { + const toOutput = (depth, line, out) => { out.push(`${' '.repeat(depth*2)}${line}`); }; @@ -4588,7 +4578,7 @@ FilterContainer.prototype.dump = function() { const fc = filterGetClass(idata); fcCounts.set(fc.name, (fcCounts.get(fc.name) || 0) + 1); const info = filterDumpInfo(idata) || ''; - toOutput(depth, info !== '' ? `${fc.name}: ${info}` : fc.name); + toOutput(depth, info !== '' ? `${fc.name}: ${info}` : fc.name, out); switch ( fc ) { case FilterBucket: case FilterCompositeAll: @@ -4618,7 +4608,7 @@ FilterContainer.prototype.dump = function() { const realms = new Map([ [ BlockAction, 'block' ], [ BlockImportant, 'block-important' ], - [ AllowAction, 'allow' ], + [ AllowAction, 'unblock' ], [ ModifyAction, 'modify' ], ]); const partyness = new Map([ @@ -4627,9 +4617,9 @@ FilterContainer.prototype.dump = function() { [ ThirdParty, '3rd-party' ], ]); for ( const [ realmBits, realmName ] of realms ) { - toOutput(1, `+ realm: ${realmName}`); + toOutput(1, `+ realm: ${realmName}`, out); for ( const [ partyBits, partyName ] of partyness ) { - toOutput(2, `+ party: ${partyName}`); + toOutput(2, `+ party: ${partyName}`, out); const processedTypeBits = new Set(); for ( const typeName in typeNameToTypeValue ) { const typeBits = typeNameToTypeValue[typeName]; @@ -4639,13 +4629,13 @@ FilterContainer.prototype.dump = function() { const ibucket = this.bitsToBucketIndices[bits]; if ( ibucket === 0 ) { continue; } const thCount = this.buckets[ibucket].size; - toOutput(3, `+ type: ${typeName} (${thCount})`); + toOutput(3, `+ type: ${typeName} (${thCount})`, out); for ( const [ th, iunit ] of this.buckets[ibucket] ) { thCounts.add(th); const ths = thConstants.has(th) ? thConstants.get(th) : `0x${th.toString(16)}`; - toOutput(4, `+ th: ${ths}`); + toOutput(4, `+ th: ${ths}`, out); dumpUnit(iunit, out, 5); } }