From adabb56dc9b3a9e047a4b8214d67700ceeec4240 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 29 Apr 2019 13:15:16 -0400 Subject: [PATCH] Do not store impossible to match filters in HNTrie Consider the two following filters: example.com www.example.com This commit make it so that if the first filter is already present in a given HNTrie, the second filter will not be stored, since HNTrie will _always_ return the first filter as a match whenever the hostname to match is example.com or any subdomain of example.com. The detection of such pointless filters is virtually free when adding a hostname to an HNTrie instance (given how data is stored in the trie), so in practice no overhead is incurred to detect such pointless filters. The ability to ignore impossible to match filters in HNTrie instances will _especially_ benefit those using large hosts files. Examples of how this helps using real configurations: - Default lists: 444 filters out of 100,382 were ignored as a result of this commit. - Default lists + "Energized Ultimate Protection": 283,669 filters out of 903,235 were ignored as a result of this commit. Side note: There was no measurable difference between the two configurations above in the performance of the matching algorithm as reported by the built-in benchmark tool. --- src/js/background.js | 2 +- src/js/hntrie.js | 27 +++++++----- src/js/wasm/hntrie.wasm | Bin 969 -> 1025 bytes src/js/wasm/hntrie.wat | 94 ++++++++++++++++++++++++++++------------ 4 files changed, 85 insertions(+), 38 deletions(-) diff --git a/src/js/background.js b/src/js/background.js index 91e9b7ef4..3c4782c39 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -138,7 +138,7 @@ const µBlock = (function() { // jshint ignore:line // Read-only systemSettings: { compiledMagic: 13, // Increase when compiled format changes - selfieMagic: 13 // Increase when selfie format changes + selfieMagic: 14 // Increase when selfie format changes }, restoreBackupSettings: { diff --git a/src/js/hntrie.js b/src/js/hntrie.js index f8ac489a5..366451af3 100644 --- a/src/js/hntrie.js +++ b/src/js/hntrie.js @@ -175,7 +175,8 @@ const HNTrieContainer = class { matchesJS(iroot) { const char0 = this.buf32[HNTRIE_CHAR0_SLOT]; let ineedle = this.buf[255]; - let icell = iroot; + let icell = this.buf32[iroot+0]; + if ( icell === 0 ) { return -1; } for (;;) { if ( ineedle === 0 ) { return -1; } ineedle -= 1; @@ -238,12 +239,6 @@ const HNTrieContainer = class { addJS(iroot) { let lhnchar = this.buf[255]; if ( lhnchar === 0 ) { return 0; } - let icell = iroot; - // special case: first node in trie - if ( this.buf32[icell+2] === 0 ) { - this.buf32[icell+2] = this.addSegment(lhnchar); - return 1; - } // grow buffer if needed if ( (this.buf32[HNTRIE_CHAR0_SLOT] - this.buf32[HNTRIE_TRIE1_SLOT]) < 24 || @@ -251,6 +246,12 @@ const HNTrieContainer = class { ) { this.growBuf(24, 256); } + let icell = this.buf32[iroot+0]; + // special case: first node in trie + if ( icell === 0 ) { + this.buf32[iroot+0] = this.addCell(0, 0, this.addSegment(lhnchar)); + return 1; + } // const char0 = this.buf32[HNTRIE_CHAR0_SLOT]; let inext; @@ -259,6 +260,9 @@ const HNTrieContainer = class { const vseg = this.buf32[icell+2]; // skip boundary cells if ( vseg === 0 ) { + // remainder is at label boundary? if yes, no need to add + // the rest since the shortest match is always reported + if ( this.buf[lhnchar-1] === 0x2E /* '.' */ ) { return -1; } icell = this.buf32[icell+1]; continue; } @@ -303,6 +307,9 @@ const HNTrieContainer = class { icell = inext; continue; } + // remainder is at label boundary? if yes, no need to add + // the rest since the shortest match is always reported + if ( this.buf[lhnchar-1] === 0x2E /* '.' */ ) { return -1; } // boundary cell + needle remainder inext = this.addCell(0, 0, 0); this.buf32[icell+1] = inext; @@ -550,7 +557,7 @@ HNTrieContainer.prototype.HNTrieRef = class { } add(hn) { - if ( this.container.setNeedle(hn).add(this.iroot) === 1 ) { + if ( this.container.setNeedle(hn).add(this.iroot) > 0 ) { this.last = -1; this.needle = ''; this.size += 1; @@ -560,7 +567,7 @@ HNTrieContainer.prototype.HNTrieRef = class { } addJS(hn) { - if ( this.container.setNeedle(hn).addJS(this.iroot) === 1 ) { + if ( this.container.setNeedle(hn).addJS(this.iroot) > 0 ) { this.last = -1; this.needle = ''; this.size += 1; @@ -570,7 +577,7 @@ HNTrieContainer.prototype.HNTrieRef = class { } addWASM(hn) { - if ( this.container.setNeedle(hn).addWASM(this.iroot) === 1 ) { + if ( this.container.setNeedle(hn).addWASM(this.iroot) > 0 ) { this.last = -1; this.needle = ''; this.size += 1; diff --git a/src/js/wasm/hntrie.wasm b/src/js/wasm/hntrie.wasm index 0324b1230c06982e0a873a3b2b7aa679459f521a..36dccfc96845c4cc30fca796c67ca91c951daae3 100644 GIT binary patch literal 1025 zcmZuwJCD;q5Z;+x+bcFFLYH!E3F#W3yMl%+(j7Wc(sTt_(@FY(xo@g0c7N4|+h`MW~A zW&)E9Ba38I=B)5Fv+>B)y8f7u3xy~|yJ~dmlcG@kCp$GKTG`- X=8+0Y*w+(MIZ-F*Y_KT+&t3Qp&U2R@ literal 969 zcmZvbzityj5XNU__q-A7D5i5ccM?(>pi4o67b!szHN7no{+wkQ&PNc{QBY7I@oYQ? z4`II9J%@mhr8|3e=9_PRv!>N}2rnKjWM77Am zEmY3^A`K@|B@JyOcHr^>oyt+;(|*4XAC%i>f+KYo$54S7gc=%4rxc_5G{0%8#4r3p zE0d~J@I6aUlGSU~*1|2rpK9-A6k_|jtRhgQWZjaE^b9$i=zfib|iR&s?VU^B!11$LE zbyYgC-S9ac9;oj&KnR;@Z;K65RI9LYM~eZq_-PI@lTdJsd`Lr*n?z>GS2+t$xN=4> ze`c-;IJCt6?gTzoVCq%Y4UDCE-vO=_<<^RO0kVBI-5oTt6-xXgch&6HEIt`0-Qm+S`3mlwM)du+IDdR>bi{s6`p_t=5tle{euL=1q9 OALlq{fy86 diff --git a/src/js/wasm/hntrie.wat b/src/js/wasm/hntrie.wat index 1154cd67a..50a3d5832 100644 --- a/src/js/wasm/hntrie.wat +++ b/src/js/wasm/hntrie.wat @@ -51,8 +51,9 @@ ;; offset. ;; (func (export "matches") - (param $icell i32) ;; offset to root cell of the trie + (param $iroot i32) ;; offset to root cell of the trie (result i32) ;; result = match index, -1 = miss + (local $icell i32) ;; offset to the current cell (local $char0 i32) ;; offset to first character data (local $ineedle i32) ;; current needle offset (local $c i32) @@ -64,15 +65,24 @@ i32.const 264 ;; start of char section is stored at addr 264 i32.load set_local $char0 - ;; $icell is an index into an array of 32-bit values - get_local $icell - i32.const 2 - i32.shl - set_local $icell ;; let ineedle = this.buf[255]; i32.const 255 ;; addr of needle is stored at addr 255 i32.load8_u set_local $ineedle + ;; let icell = this.buf32[iroot+0]; + get_local $iroot + i32.const 2 + i32.shl + i32.load + i32.const 2 + i32.shl + tee_local $icell + ;; if ( icell === 0 ) { return -1; } + i32.eqz + if + i32.const -1 + return + end ;; for (;;) { block $noSegment loop $nextSegment ;; if ( ineedle === 0 ) { return -1; } @@ -244,8 +254,9 @@ ;; Add a new hostname to a trie which root cell is passed as argument. ;; (func (export "add") - (param $icell i32) ;; index of root cell of the trie + (param $iroot i32) ;; index of root cell of the trie (result i32) ;; result: 0 not added, 1 = added + (local $icell i32) ;; index of current cell in the trie (local $lhnchar i32) ;; number of characters left to process in hostname (local $char0 i32) ;; offset to start of character data section (local $vseg i32) ;; integer value describing a segment @@ -264,24 +275,6 @@ i32.const 0 return end - ;; let icell = iroot; - get_local $icell - i32.const 2 - i32.shl - tee_local $icell - ;; if ( this.buf32[icell+2] === 0 ) { - i32.load offset=8 - i32.eqz - if - ;;this.buf32[icell+2] = this.addSegment(lhnchar); - ;; return 1; - get_local $icell - get_local $lhnchar - call $addSegment - i32.store offset=8 - i32.const 1 - return - end ;; if ( ;; (this.buf32[HNBIGTRIE_CHAR0_SLOT] - this.buf32[HNBIGTRIE_TRIE1_SLOT]) < 24 || ;; (this.buf.length - this.buf32[HNBIGTRIE_CHAR1_SLOT]) < 256 @@ -310,6 +303,30 @@ call $growBuf end end + ;; let icell = this.buf32[iroot+0]; + get_local $iroot + i32.const 2 + i32.shl + tee_local $iroot + i32.load + i32.const 2 + i32.shl + tee_local $icell + ;; if ( this.buf32[icell+2] === 0 ) { + i32.eqz + if + ;; this.buf32[iroot+0] = this.addCell(0, 0, this.addSegment(lhnchar)); + ;; return 1; + get_local $iroot + i32.const 0 + i32.const 0 + get_local $lhnchar + call $addSegment + call $addCell + i32.store + i32.const 1 + return + end ;; const char0 = this.buf32[HNBIGTRIE_CHAR0_SLOT]; i32.const 264 i32.load @@ -323,6 +340,19 @@ ;; if ( vseg === 0 ) { i32.eqz if + ;; if ( this.buf[lhnchar-1] === 0x2E /* '.' */ ) { return -1; } + get_local $lhnchar + i32.const -1 + i32.add + i32.load8_u + i32.const 0x2E + i32.eq + if + i32.const -1 + return + end + ;; icell = this.buf32[icell+1]; + ;; continue; get_local $icell i32.load offset=4 i32.const 2 @@ -463,13 +493,23 @@ else ;; if ( inext !== 0 ) { get_local $inext - i32.eqz - if else + if ;; icell = inext; get_local $inext set_local $icell br $nextSegment end + ;; if ( this.buf[lhnchar-1] === 0x2E /* '.' */ ) { return -1; } + get_local $lhnchar + i32.const -1 + i32.add + i32.load8_u + i32.const 0x2E + i32.eq + if + i32.const -1 + return + end ;; inext = this.addCell(0, 0, 0); ;; this.buf32[icell+1] = inext; get_local $icell