uBlock/docs/tests/hntrie-test.html

225 lines
6.3 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body style="font: 14px sans-serif">
<h1>HNTrieContainer test</h1>
<div><button id="test" type="button">Test!</button></div>
<div id="stdout"></div>
<script src="https://rawcdn.githack.com/gorhill/uBlock/1b6fea16da81d1df3e2efd5a31894f71ea04dbb1/src/js/hntrie.js"></script>
<!-- <script src="../../src/js/hntrie.js"></script> -->
<script src="hostname-pool.js"></script>
<script>
const createRandomLabel = function() {
return Math.random().toString(36).slice(2);
};
const stdout = function(s) {
const line = document.createElement('div');
const parent = document.getElementById('stdout');
let tooManyErrors = false;
if ( parent.childElementCount > 100 ) {
tooManyErrors = true;
line.textContent = 'Too many errors, aborting';
} else {
line.textContent = s;
}
parent.appendChild(line)
if ( tooManyErrors ) {
throw 'Aborting';
}
}
/******************************************************************************/
// Dictionary of hostnames
//
const FilterHostnameDict = function(hostnames) {
this.h = ''; // short-lived register
this.dict = new Set();
if ( hostnames !== undefined ) {
this.fromIterable(hostnames);
}
};
FilterHostnameDict.prototype = {
add: function(hn) {
if ( this.dict.has(hn) ) { return false; }
this.dict.add(hn);
return true;
},
fromIterable: function(hostnames) {
for ( let hn of hostnames ) {
this.add(hn);
}
return this;
},
matches: function(needle) {
while ( this.dict.has(needle) === false ) {
const pos = needle.indexOf('.');
if ( pos === -1 ) {
this.h = '';
return false;
}
needle = needle.slice(pos + 1);
}
this.h = needle;
return true;
},
};
/******************************************************************************/
const testFlavor = function(hostnames, name, matchesFn, hitFn) {
stdout('\xA0');
stdout('Testing ' + name + '...');
const t0 = performance.now();
for ( let i = 0; i < hostnames.length; i++ ) {
// Exact hits
let needle = hostnames[i];
if ( hitFn(matchesFn(needle)) === false ) {
stdout('Exact hits failed: ' + needle);
}
// Subdomain hits
needle = createRandomLabel() + '.' + hostnames[i];
if ( hitFn(matchesFn(needle)) === false ) {
stdout('Subdomain hits failed: ' + needle);
}
// Misses batch 1
needle = createRandomLabel() + '.com';
if ( hitFn(matchesFn(needle)) !== false ) {
stdout('Misses batch 1: ' + needle);
}
// Misses batch 2
needle = hostnames[i] + '.' + createRandomLabel();
if ( hitFn(matchesFn(needle)) !== false ) {
stdout('Misses batch 2: ' + needle);
}
// Misses batch 3
needle = hostnames[i];
let pos = needle.lastIndexOf('.');
if ( pos !== -1 ) {
needle = needle.slice(0, pos) + needle.slice(pos + 1);
if ( hitFn(matchesFn(needle)) !== false ) {
stdout('Misses batch 3: ' + needle);
}
}
}
const t1 = performance.now();
stdout(
name + ': ' +
(hostnames.length * 5).toLocaleString() +
' tests completed in ' +
(t1 - t0).toFixed(2) + ' ms'
);
};
const hnBigTrieJS = new HNTrieContainer();
const hnBigTrieWASM = new HNTrieContainer();
const hnBigTrieUnserialized = new HNTrieContainer();
Promise.all([
hnBigTrieJS.readyToUse(),
hnBigTrieWASM.readyToUse()
]).then(( ) => {
let t0 = performance.now();
const theSet = new FilterHostnameDict(hostnamePool);
let t1 = performance.now();
stdout('\xA0');
stdout(
'Set creation completed in ' +
(t1 - t0).toFixed(2) + ' ms'
);
t0 = performance.now();
const theTrieJS = hnBigTrieJS.fromIterable(hostnamePool, 'addJS');
hnBigTrieJS.optimize();
t1 = performance.now();
stdout('\xA0');
stdout(
'HNTrieContainer creation (JS) completed in ' +
(t1 - t0).toFixed(2) + ' ms'
);
let theTrieWASM;
if ( hnBigTrieWASM.addWASM instanceof Function ) {
t0 = performance.now();
theTrieWASM = hnBigTrieWASM.fromIterable(hostnamePool, 'addWASM');
hnBigTrieWASM.optimize();
t1 = performance.now();
stdout('\xA0');
stdout(
'HNTrieContainer creation (WASM) completed in ' +
(t1 - t0).toFixed(2) + ' ms'
);
const bufJS = theTrieJS.container.buf;
const bufWASM = theTrieWASM.container.buf;
for ( let i = 0; i < bufJS.length; i++ ) {
if ( bufJS[i] !== bufWASM[i] ) {
stdout('theTrieWASM failure at index ' + i);
break;
}
}
}
let selfie = hnBigTrieJS.serialize();
t0 = performance.now();
hnBigTrieUnserialized.unserialize(selfie);
const theTrieUnserialized = hnBigTrieUnserialized.createOne(hnBigTrieJS.compileOne(theTrieJS));
t1 = performance.now();
stdout('\xA0');
stdout(
'HNTrieContainer creation (unserialized) completed in ' +
(t1 - t0).toFixed(2) + ' ms'
);
selfie = undefined;
document.getElementById('test').addEventListener('click', ( ) => {
let parent = document.getElementById('stdout');
while ( parent.childElementCount !== 0 ) {
parent.removeChild(parent.firstChild);
}
testFlavor(
hostnamePool,
'Set (JS)',
theSet.matches.bind(theSet),
r => r
);
testFlavor(
hostnamePool,
'HNTrieContainer (JS)',
theTrieJS.matchesJS.bind(theTrieJS),
r => r >= 0
);
if ( theTrieWASM !== undefined ) {
testFlavor(
hostnamePool,
'HNTrieContainer (WASM)',
theTrieWASM.matchesWASM.bind(theTrieWASM),
r => r >= 0
);
}
testFlavor(
hostnamePool,
'HNTrieContainer (unserialized)',
theTrieUnserialized.matchesJS.bind(theTrieUnserialized),
r => r >= 0
);
});
});
</script>
</body>
</html>