mirror of
https://github.com/gorhill/uBlock.git
synced 2024-11-07 03:12:33 +01:00
Add WASM versions for some bidi-trie methods
Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/761 Changes related to above issue made it possible to create WASM versions of methods used in the bidi-trie. In this commit, WASM versions for startsWith(), indexOf() and lastIndexOf() have been implemented.
This commit is contained in:
parent
dd2a9faa4c
commit
b0cbc47d9a
@ -50,6 +50,7 @@ const Important = 1 << 1;
|
||||
const AnyParty = 0 << 2;
|
||||
const FirstParty = 1 << 2;
|
||||
const ThirdParty = 2 << 2;
|
||||
const BlockImportant = BlockAction | Important;
|
||||
|
||||
const typeNameToTypeValue = {
|
||||
'no_type': 0 << 4,
|
||||
@ -159,8 +160,6 @@ const toNormalizedType = {
|
||||
'websocket': 'websocket',
|
||||
};
|
||||
|
||||
const BlockImportant = BlockAction | Important;
|
||||
|
||||
const typeValueFromCatBits = catBits => (catBits >>> 4) & 0b11111;
|
||||
|
||||
/******************************************************************************/
|
||||
@ -320,7 +319,11 @@ const bidiTrie = (( ) => {
|
||||
);
|
||||
} catch(ex) {
|
||||
}
|
||||
return new µb.BidiTrieContainer(trieDetails, bidiTrieMatchExtra);
|
||||
const trie = new µb.BidiTrieContainer(trieDetails, bidiTrieMatchExtra);
|
||||
if ( µb.hiddenSettings.disableWebAssembly !== true ) {
|
||||
trie.enableWASM();
|
||||
}
|
||||
return trie;
|
||||
})();
|
||||
|
||||
const bidiTrieOptimize = function() {
|
||||
@ -498,7 +501,14 @@ const FilterPatternPlain = class {
|
||||
|
||||
match() {
|
||||
const left = $tokenBeg;
|
||||
if ( bidiTrie.startsWith(left, this.i, this.n) === false ) {
|
||||
if (
|
||||
bidiTrie.startsWith(
|
||||
left,
|
||||
bidiTrie.haystackLen,
|
||||
this.i,
|
||||
this.n
|
||||
) === 0
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
$patternMatchLeft = left;
|
||||
@ -561,7 +571,14 @@ registerFilterClass(FilterPatternPlain);
|
||||
const FilterPatternPlain1 = class extends FilterPatternPlain {
|
||||
match() {
|
||||
const left = $tokenBeg - 1;
|
||||
if ( bidiTrie.startsWith(left, this.i, this.n) === false ) {
|
||||
if (
|
||||
bidiTrie.startsWith(
|
||||
left,
|
||||
bidiTrie.haystackLen,
|
||||
this.i,
|
||||
this.n
|
||||
) === 0
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
$patternMatchLeft = left;
|
||||
@ -581,7 +598,14 @@ const FilterPatternPlainX = class extends FilterPatternPlain {
|
||||
|
||||
match() {
|
||||
const left = $tokenBeg - this.tokenBeg;
|
||||
if ( bidiTrie.startsWith(left, this.i, this.n) === false ) {
|
||||
if (
|
||||
bidiTrie.startsWith(
|
||||
left,
|
||||
bidiTrie.haystackLen,
|
||||
this.i,
|
||||
this.n
|
||||
) === 0
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
$patternMatchLeft = left;
|
||||
@ -686,7 +710,7 @@ const FilterPatternRight = class {
|
||||
|
||||
match() {
|
||||
const right = bidiTrie.lastIndexOf(
|
||||
$patternMatchRight, bidiTrie.haystackSize,
|
||||
$patternMatchRight, bidiTrie.haystackLen,
|
||||
this.i, this.n
|
||||
);
|
||||
if ( right === -1 ) { return false; }
|
||||
@ -729,7 +753,7 @@ const FilterPatternRightEx = class extends FilterPatternRight {
|
||||
match() {
|
||||
const left = $patternMatchRight;
|
||||
const right = bidiTrie.lastIndexOf(
|
||||
left + 1, bidiTrie.haystackSize,
|
||||
left + 1, bidiTrie.haystackLen,
|
||||
this.i, this.n
|
||||
);
|
||||
if ( right === -1 ) { return false; }
|
||||
@ -881,7 +905,7 @@ const FilterAnchorHn = class {
|
||||
this.lastBeg = len !== 0 ? haystackCodes.indexOf(0x3A) : -1;
|
||||
if ( this.lastBeg !== -1 ) {
|
||||
if (
|
||||
this.lastBeg >= bidiTrie.haystackSize ||
|
||||
this.lastBeg >= bidiTrie.haystackLen ||
|
||||
haystackCodes[this.lastBeg+1] !== 0x2F ||
|
||||
haystackCodes[this.lastBeg+2] !== 0x2F
|
||||
) {
|
||||
|
263
src/js/strie.js
263
src/js/strie.js
@ -19,6 +19,8 @@
|
||||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* globals WebAssembly */
|
||||
|
||||
'use strict';
|
||||
|
||||
// *****************************************************************************
|
||||
@ -131,20 +133,21 @@ const roundToPageSize = v => (v + PAGE_SIZE-1) & ~(PAGE_SIZE-1);
|
||||
constructor(details, extraHandler) {
|
||||
if ( details instanceof Object === false ) { details = {}; }
|
||||
const len = roundToPageSize(details.byteLength || 0);
|
||||
const minInitialLen = PAGE_SIZE * 4;
|
||||
this.buf8 = new Uint8Array(Math.max(len, minInitialLen));
|
||||
const minInitialSize = PAGE_SIZE * 4;
|
||||
this.buf8 = new Uint8Array(Math.max(len, minInitialSize));
|
||||
this.buf32 = new Uint32Array(this.buf8.buffer);
|
||||
this.buf32[TRIE0_SLOT] = TRIE0_START;
|
||||
this.buf32[TRIE1_SLOT] = this.buf32[TRIE0_SLOT];
|
||||
this.buf32[CHAR0_SLOT] = details.char0 || (minInitialLen >>> 1);
|
||||
this.buf32[CHAR0_SLOT] = details.char0 || (minInitialSize >>> 1);
|
||||
this.buf32[CHAR1_SLOT] = this.buf32[CHAR0_SLOT];
|
||||
this.haystack = this.buf8.subarray(
|
||||
HAYSTACK_START,
|
||||
HAYSTACK_START + HAYSTACK_SIZE
|
||||
);
|
||||
this.haystackSize = 0;
|
||||
this.haystackLen = 0;
|
||||
this.extraHandler = extraHandler;
|
||||
this.textDecoder = null;
|
||||
this.wasmMemory = null;
|
||||
|
||||
this.$l = 0;
|
||||
this.$r = 0;
|
||||
@ -164,7 +167,7 @@ const roundToPageSize = v => (v + PAGE_SIZE-1) & ~(PAGE_SIZE-1);
|
||||
const buf32 = this.buf32;
|
||||
const buf8 = this.buf8;
|
||||
const char0 = buf32[CHAR0_SLOT];
|
||||
const aR = this.haystackSize;
|
||||
const aR = this.haystackLen;
|
||||
let icell = iroot;
|
||||
let al = i;
|
||||
let c, v, bl, n;
|
||||
@ -180,15 +183,16 @@ const roundToPageSize = v => (v + PAGE_SIZE-1) & ~(PAGE_SIZE-1);
|
||||
if ( icell === 0 ) { return false; }
|
||||
}
|
||||
// all characters in segment must match
|
||||
n = v >>> 24;
|
||||
if ( n > 1 ) {
|
||||
n -= 1;
|
||||
if ( (al + n) > aR ) { return false; }
|
||||
bl += 1;
|
||||
for ( let i = 0; i < n; i++ ) {
|
||||
if ( buf8[al+i] !== buf8[bl+i] ) { return false; }
|
||||
}
|
||||
al += n;
|
||||
n = (v >>> 24) - 1;
|
||||
if ( n !== 0 ) {
|
||||
const ar = al + n;
|
||||
if ( ar > aR ) { return false; }
|
||||
let i = al, j = bl + 1;
|
||||
do {
|
||||
if ( buf8[i] !== buf8[j] ) { return false; }
|
||||
i += 1; j += 1;
|
||||
} while ( i !== ar );
|
||||
al = i;
|
||||
}
|
||||
// next segment
|
||||
icell = buf32[icell+CELL_AND];
|
||||
@ -225,20 +229,22 @@ const roundToPageSize = v => (v + PAGE_SIZE-1) & ~(PAGE_SIZE-1);
|
||||
// find first segment with a first-character match
|
||||
for (;;) {
|
||||
v = buf32[icell+SEGMENT_INFO];
|
||||
n = v >>> 24;
|
||||
br = char0 + (v & 0x00FFFFFF) + n - 1;
|
||||
n = (v >>> 24) - 1;
|
||||
br = char0 + (v & 0x00FFFFFF) + n;
|
||||
if ( buf8[br] === c ) { break; }
|
||||
icell = buf32[icell+CELL_OR];
|
||||
if ( icell === 0 ) { return false; }
|
||||
}
|
||||
// all characters in segment must match
|
||||
if ( n > 1 ) {
|
||||
n -= 1;
|
||||
if ( n > ar ) { return false; }
|
||||
for ( let i = 1; i <= n; i++ ) {
|
||||
if ( buf8[ar-i] !== buf8[br-i] ) { return false; }
|
||||
}
|
||||
ar -= n;
|
||||
if ( n !== 0 ) {
|
||||
const al = ar - n;
|
||||
if ( al < 0 ) { return false; }
|
||||
let i = ar, j = br;
|
||||
do {
|
||||
i -= 1; j -= 1;
|
||||
if ( buf8[i] !== buf8[j] ) { return false; }
|
||||
} while ( i !== al );
|
||||
ar = i;
|
||||
}
|
||||
// next segment
|
||||
icell = buf32[icell+CELL_AND];
|
||||
@ -605,75 +611,101 @@ const roundToPageSize = v => (v + PAGE_SIZE-1) & ~(PAGE_SIZE-1);
|
||||
}
|
||||
|
||||
// WASMable.
|
||||
startsWith(haystackOffset, needleOffset, needleLen) {
|
||||
if ( (haystackOffset + needleLen) > this.haystackSize ) {
|
||||
return false;
|
||||
startsWith(haystackLeft, haystackRight, needleLeft, needleLen) {
|
||||
if ( haystackLeft < 0 || (haystackLeft + needleLen) > haystackRight ) {
|
||||
return 0;
|
||||
}
|
||||
const haystackCodes = this.haystack;
|
||||
const needleCodes = this.buf8;
|
||||
needleOffset += this.buf32[CHAR0_SLOT];
|
||||
for ( let i = 0; i < needleLen; i++ ) {
|
||||
if (
|
||||
haystackCodes[haystackOffset+i] !==
|
||||
needleCodes[needleOffset+i]
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const charCodes = this.buf8;
|
||||
needleLeft += this.buf32[CHAR0_SLOT];
|
||||
const needleRight = needleLeft + needleLen;
|
||||
while ( charCodes[haystackLeft] === charCodes[needleLeft] ) {
|
||||
needleLeft += 1;
|
||||
if ( needleLeft === needleRight ) { return 1; }
|
||||
haystackLeft += 1;
|
||||
}
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Find the left-most instance of substring in main string
|
||||
// WASMable.
|
||||
indexOf(haystackBeg, haystackEnd, needleOffset, needleLen) {
|
||||
indexOf(haystackLeft, haystackEnd, needleLeft, needleLen) {
|
||||
haystackEnd -= needleLen;
|
||||
if ( haystackEnd < haystackBeg ) { return -1; }
|
||||
const haystackCodes = this.haystack;
|
||||
const needleCodes = this.buf8;
|
||||
needleOffset += this.buf32[CHAR0_SLOT];
|
||||
let i = haystackBeg;
|
||||
let c0 = needleCodes[needleOffset];
|
||||
if ( haystackEnd < haystackLeft ) { return -1; }
|
||||
needleLeft += this.buf32[CHAR0_SLOT];
|
||||
const needleRight = needleLeft + needleLen;
|
||||
const charCodes = this.buf8;
|
||||
for (;;) {
|
||||
i = haystackCodes.indexOf(c0, i);
|
||||
if ( i === -1 || i > haystackEnd ) { return -1; }
|
||||
let j = 1;
|
||||
while ( j < needleLen ) {
|
||||
if ( haystackCodes[i+j] !== needleCodes[needleOffset+j] ) {
|
||||
break;
|
||||
}
|
||||
let i = haystackLeft;
|
||||
let j = needleLeft;
|
||||
while ( charCodes[i] === charCodes[j] ) {
|
||||
j += 1;
|
||||
if ( j === needleRight ) { return haystackLeft; }
|
||||
i += 1;
|
||||
}
|
||||
if ( j === needleLen ) { return i; }
|
||||
i += 1;
|
||||
haystackLeft += 1;
|
||||
if ( haystackLeft === haystackEnd ) { break; }
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Find the right-most instance of substring in main string.
|
||||
// WASMable.
|
||||
lastIndexOf(haystackBeg, haystackEnd, needleOffset, needleLen) {
|
||||
needleOffset += this.buf32[CHAR0_SLOT];
|
||||
let i = haystackEnd - needleLen;
|
||||
if ( i < haystackBeg ) { return -1; }
|
||||
const haystackCodes = this.haystack;
|
||||
const needleCodes = this.buf8;
|
||||
let c0 = needleCodes[needleOffset];
|
||||
lastIndexOf(haystackBeg, haystackEnd, needleLeft, needleLen) {
|
||||
let haystackLeft = haystackEnd - needleLen;
|
||||
if ( haystackLeft < haystackBeg ) { return -1; }
|
||||
needleLeft += this.buf32[CHAR0_SLOT];
|
||||
const needleRight = needleLeft + needleLen;
|
||||
const charCodes = this.buf8;
|
||||
for (;;) {
|
||||
i = haystackCodes.lastIndexOf(c0, i);
|
||||
if ( i === -1 || i < haystackBeg ) { return -1; }
|
||||
let j = 1;
|
||||
while ( j < needleLen ) {
|
||||
if ( haystackCodes[i+j] !== needleCodes[needleOffset+j] ) {
|
||||
break;
|
||||
}
|
||||
let i = haystackLeft;
|
||||
let j = needleLeft;
|
||||
while ( charCodes[i] === charCodes[j] ) {
|
||||
j += 1;
|
||||
if ( j === needleRight ) { return haystackLeft; }
|
||||
i += 1;
|
||||
}
|
||||
if ( j === needleLen ) { return i; }
|
||||
i -= 1;
|
||||
if ( haystackLeft === haystackBeg ) { break; }
|
||||
haystackLeft -= 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
async enableWASM() {
|
||||
if ( this.wasmMemory instanceof WebAssembly.Memory ) { return true; }
|
||||
const module = await getWasmModule();
|
||||
if ( module instanceof WebAssembly.Module === false ) {
|
||||
return false;
|
||||
}
|
||||
const memory = new WebAssembly.Memory({
|
||||
initial: this.buf8.length >>> 16
|
||||
});
|
||||
const instance = await WebAssembly.instantiate(
|
||||
module,
|
||||
{ imports: { memory } }
|
||||
);
|
||||
if ( instance instanceof WebAssembly.Instance === false ) {
|
||||
return false;
|
||||
}
|
||||
this.wasmMemory = memory;
|
||||
const curPageCount = memory.buffer.byteLength >>> 16;
|
||||
const newPageCount = this.buf8.byteLength + PAGE_SIZE-1 >>> 16;
|
||||
if ( newPageCount > curPageCount ) {
|
||||
memory.grow(newPageCount - curPageCount);
|
||||
}
|
||||
const buf8 = new Uint8Array(memory.buffer);
|
||||
buf8.set(this.buf8);
|
||||
this.buf8 = buf8;
|
||||
this.buf32 = new Uint32Array(this.buf8.buffer);
|
||||
this.haystack = this.buf8.subarray(
|
||||
HAYSTACK_START,
|
||||
HAYSTACK_START + HAYSTACK_SIZE
|
||||
);
|
||||
this.startsWith = instance.exports.startsWith;
|
||||
this.indexOf = instance.exports.indexOf;
|
||||
this.lastIndexOf = instance.exports.lastIndexOf;
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private methods
|
||||
//--------------------------------------------------------------------------
|
||||
@ -710,6 +742,7 @@ const roundToPageSize = v => (v + PAGE_SIZE-1) & ~(PAGE_SIZE-1);
|
||||
}
|
||||
|
||||
shrinkBuf() {
|
||||
if ( this.wasmMemory !== null ) { return; }
|
||||
const char0 = this.buf32[TRIE1_SLOT] + MIN_FREE_CELL_BYTE_LENGTH;
|
||||
const char1 = char0 + this.buf32[CHAR1_SLOT] - this.buf32[CHAR0_SLOT];
|
||||
const bufLen = char1 + 256;
|
||||
@ -723,9 +756,25 @@ const roundToPageSize = v => (v + PAGE_SIZE-1) & ~(PAGE_SIZE-1);
|
||||
}
|
||||
const charDataLen = this.buf32[CHAR1_SLOT] - this.buf32[CHAR0_SLOT];
|
||||
if ( bufLen !== this.buf8.length ) {
|
||||
const newBuf = new Uint8Array(bufLen);
|
||||
let newBuf;
|
||||
if ( this.wasmMemory === null ) {
|
||||
newBuf = new Uint8Array(bufLen);
|
||||
} else {
|
||||
const oldPageCount = this.buf8.length >>> 16;
|
||||
const newPageCount = (bufLen + 0xFFFF) >>> 16;
|
||||
if ( newPageCount > oldPageCount ) {
|
||||
this.wasmMemory.grow(newPageCount - oldPageCount);
|
||||
}
|
||||
newBuf = new Uint8Array(this.wasmMemory.buffer);
|
||||
}
|
||||
newBuf.set(this.buf8.subarray(0, this.buf32[TRIE1_SLOT]), 0);
|
||||
newBuf.set(this.buf8.subarray(this.buf32[CHAR0_SLOT], this.buf32[CHAR1_SLOT]), char0);
|
||||
newBuf.set(
|
||||
this.buf8.subarray(
|
||||
this.buf32[CHAR0_SLOT],
|
||||
this.buf32[CHAR1_SLOT]
|
||||
),
|
||||
char0
|
||||
);
|
||||
this.buf8 = newBuf;
|
||||
this.buf32 = new Uint32Array(this.buf8.buffer);
|
||||
this.buf32[CHAR0_SLOT] = char0;
|
||||
@ -736,7 +785,11 @@ const roundToPageSize = v => (v + PAGE_SIZE-1) & ~(PAGE_SIZE-1);
|
||||
);
|
||||
}
|
||||
if ( char0 !== this.buf32[CHAR0_SLOT] ) {
|
||||
this.buf8.copyWithin(char0, this.buf32[CHAR0_SLOT], this.buf32[CHAR1_SLOT]);
|
||||
this.buf8.copyWithin(
|
||||
char0,
|
||||
this.buf32[CHAR0_SLOT],
|
||||
this.buf32[CHAR1_SLOT]
|
||||
);
|
||||
this.buf32[CHAR0_SLOT] = char0;
|
||||
this.buf32[CHAR1_SLOT] = char0 + charDataLen;
|
||||
}
|
||||
@ -839,6 +892,72 @@ const roundToPageSize = v => (v + PAGE_SIZE-1) & ~(PAGE_SIZE-1);
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Code below is to attempt to load a WASM module which implements:
|
||||
//
|
||||
// - BidiTrieContainer.startsWith()
|
||||
//
|
||||
// The WASM module is entirely optional, the JS implementations will be
|
||||
// used should the WASM module be unavailable for whatever reason.
|
||||
|
||||
const getWasmModule = (( ) => {
|
||||
let wasmModulePromise;
|
||||
|
||||
return function() {
|
||||
if ( wasmModulePromise instanceof Promise ) {
|
||||
return wasmModulePromise;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof WebAssembly !== 'object' ||
|
||||
typeof WebAssembly.compileStreaming !== 'function'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Soft-dependency on vAPI so that the code here can be used outside of
|
||||
// uBO (i.e. tests, benchmarks)
|
||||
if (
|
||||
typeof vAPI === 'object' &&
|
||||
vAPI.webextFlavor.soup.has('firefox') === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The wasm module will work only if CPU is natively little-endian,
|
||||
// as we use native uint32 array in our js code.
|
||||
const uint32s = new Uint32Array(1);
|
||||
const uint8s = new Uint8Array(uint32s.buffer);
|
||||
uint32s[0] = 1;
|
||||
if ( uint8s[0] !== 1 ) { return; }
|
||||
|
||||
// The directory from which the current script was fetched should also
|
||||
// contain the related WASM file. The script is fetched from a trusted
|
||||
// location, and consequently so will be the related WASM file.
|
||||
let workingDir;
|
||||
{
|
||||
const url = new URL(document.currentScript.src);
|
||||
const match = /[^\/]+$/.exec(url.pathname);
|
||||
if ( match !== null ) {
|
||||
url.pathname = url.pathname.slice(0, match.index);
|
||||
}
|
||||
workingDir = url.href;
|
||||
}
|
||||
|
||||
wasmModulePromise = fetch(
|
||||
workingDir + 'wasm/biditrie.wasm',
|
||||
{ mode: 'same-origin' }
|
||||
).then(
|
||||
WebAssembly.compileStreaming
|
||||
).catch(reason => {
|
||||
log.info(reason);
|
||||
});
|
||||
|
||||
return wasmModulePromise;
|
||||
};
|
||||
})();
|
||||
|
||||
// end of local namespace
|
||||
// *****************************************************************************
|
||||
|
||||
|
@ -155,7 +155,7 @@
|
||||
url = url.slice(0, 2048);
|
||||
l = 2048;
|
||||
}
|
||||
encodeInto.haystackSize = l;
|
||||
encodeInto.haystackLen = l;
|
||||
const knownTokens = this.knownTokens;
|
||||
const vtc = this._validTokenChars;
|
||||
const charCodes = encodeInto.haystack;
|
||||
|
BIN
src/js/wasm/biditrie.wasm
Normal file
BIN
src/js/wasm/biditrie.wasm
Normal file
Binary file not shown.
311
src/js/wasm/biditrie.wat
Normal file
311
src/js/wasm/biditrie.wat
Normal file
@ -0,0 +1,311 @@
|
||||
;;
|
||||
;; uBlock Origin - a browser extension to block requests.
|
||||
;; Copyright (C) 2019-present Raymond Hill
|
||||
;;
|
||||
;; This program is free software: you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
;;
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
;;
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
;;
|
||||
;; Home: https://github.com/gorhill/uBlock
|
||||
;; File: biditrie.wat
|
||||
;; Description: WebAssembly code used by src/js/biditrie.js
|
||||
;; How to compile: See README.md in this directory.
|
||||
|
||||
(module
|
||||
;;
|
||||
;; module start
|
||||
;;
|
||||
|
||||
(memory (import "imports" "memory") 1)
|
||||
|
||||
;; Trie container
|
||||
;;
|
||||
;; Memory layout, byte offset:
|
||||
;; HAYSTACK_START = 0;
|
||||
;; HAYSTACK_SIZE = 2048; // i32 / i8
|
||||
;; HAYSTACK_SIZE_SLOT = HAYSTACK_SIZE >>> 2; // 512 / 2048
|
||||
;; TRIE0_SLOT = HAYSTACK_SIZE_SLOT + 1; // 512 / 2052
|
||||
;; TRIE1_SLOT = HAYSTACK_SIZE_SLOT + 2; // 513 / 2056
|
||||
;; CHAR0_SLOT = HAYSTACK_SIZE_SLOT + 3; // 514 / 2060
|
||||
;; CHAR1_SLOT = HAYSTACK_SIZE_SLOT + 4; // 515 / 2064
|
||||
;; TRIE0_START = HAYSTACK_SIZE_SLOT + 5 << 2; // 2068
|
||||
;;
|
||||
|
||||
;;
|
||||
;; Public functions
|
||||
;;
|
||||
|
||||
;;
|
||||
;; unsigned int startsWith(haystackLeft, haystackRight, needleLeft, needleLen)
|
||||
;;
|
||||
;; Test whether the string at needleOffset and of length needleLen matches
|
||||
;; the haystack at offset haystackOffset.
|
||||
;;
|
||||
(func (export "startsWith")
|
||||
(param $haystackLeft i32) ;; start offset in haystack
|
||||
(param $haystackRight i32) ;; end offset in haystack
|
||||
(param $needleLeft i32) ;; start of needle in character buffer
|
||||
(param $needleLen i32) ;; number of characters to match
|
||||
(result i32) ;; result: 0 = no match, 1 = match
|
||||
(local $needleRight i32)
|
||||
;;
|
||||
;; if ( haystackLeft < 0 || (haystackLeft + needleLen) > haystackRight ) {
|
||||
;; return 0;
|
||||
;; }
|
||||
get_local $haystackLeft
|
||||
i32.const 0
|
||||
i32.lt_s
|
||||
if
|
||||
i32.const 0
|
||||
return
|
||||
end
|
||||
get_local $haystackLeft
|
||||
get_local $needleLen
|
||||
i32.add
|
||||
get_local $haystackRight
|
||||
i32.gt_u
|
||||
if
|
||||
i32.const 0
|
||||
return
|
||||
end
|
||||
;; const charCodes = this.buf8;
|
||||
;; needleLeft += this.buf32[CHAR0_SLOT];
|
||||
get_local $needleLeft
|
||||
i32.const 2060 ;; CHAR0_SLOT memory address
|
||||
i32.load ;; CHAR0 memory address
|
||||
i32.add ;; needle memory address
|
||||
;; const needleRight = needleLeft + needleLen;
|
||||
tee_local $needleLeft
|
||||
get_local $needleLen
|
||||
i32.add
|
||||
set_local $needleRight
|
||||
;; while ( charCodes[haystackLeft] === charCodes[needleLeft] ) {
|
||||
block $breakCompare loop $compare
|
||||
get_local $haystackLeft
|
||||
i32.load8_u
|
||||
get_local $needleLeft
|
||||
i32.load8_u
|
||||
i32.ne
|
||||
br_if $breakCompare
|
||||
;; needleLeft += 1;
|
||||
get_local $needleLeft
|
||||
i32.const 1
|
||||
i32.add
|
||||
tee_local $needleLeft
|
||||
;; if ( needleLeft === needleRight ) { break; }
|
||||
get_local $needleRight
|
||||
i32.eq
|
||||
if
|
||||
i32.const 1
|
||||
return
|
||||
end
|
||||
;; haystackLeft += 1;
|
||||
i32.const 1
|
||||
get_local $haystackLeft
|
||||
i32.add
|
||||
set_local $haystackLeft
|
||||
br $compare
|
||||
end end
|
||||
;; }
|
||||
;; return true;
|
||||
i32.const 1
|
||||
)
|
||||
|
||||
;;
|
||||
;; int indexOf(haystackLeft, haystackEnd, needleLeft, needleLen)
|
||||
;;
|
||||
;; Test whether the string at needleOffset and of length needleLen is found in
|
||||
;; the haystack at or to the left of haystackLeft, but not farther than
|
||||
;; haystackEnd.
|
||||
;;
|
||||
(func (export "indexOf")
|
||||
(param $haystackLeft i32) ;; start offset in haystack
|
||||
(param $haystackEnd i32) ;; end offset in haystack
|
||||
(param $needleLeft i32) ;; start of needle in character buffer
|
||||
(param $needleLen i32) ;; number of characters to match
|
||||
(result i32) ;; result: index of match, -1 = no match
|
||||
(local $needleRight i32)
|
||||
(local $i i32)
|
||||
(local $j i32)
|
||||
(local $c0 i32)
|
||||
;; haystackEnd -= needleLen;
|
||||
get_local $haystackEnd
|
||||
get_local $needleLen
|
||||
i32.sub
|
||||
tee_local $haystackEnd
|
||||
;; if ( haystackEnd < haystackLeft ) { return -1; }
|
||||
get_local $haystackLeft
|
||||
i32.lt_s
|
||||
if
|
||||
i32.const -1
|
||||
return
|
||||
end
|
||||
;; needleLeft += this.buf32[CHAR0_SLOT];
|
||||
get_local $needleLeft
|
||||
i32.const 2060 ;; CHAR0_SLOT memory address
|
||||
i32.load ;; CHAR0 memory address
|
||||
i32.add ;; needle memory address
|
||||
tee_local $needleLeft
|
||||
;; const needleRight = needleLeft + needleLen;
|
||||
get_local $needleLen
|
||||
i32.add
|
||||
set_local $needleRight
|
||||
;; const charCodes = this.buf8;
|
||||
;; for (;;) {
|
||||
block $breakMainLoop loop $mainLoop
|
||||
;; let i = haystackLeft;
|
||||
;; let j = needleLeft;
|
||||
get_local $haystackLeft
|
||||
set_local $i
|
||||
get_local $needleLeft
|
||||
set_local $j
|
||||
;; while ( charCodes[i] === charCodes[j] ) {
|
||||
block $breakMatchChars loop $matchChars
|
||||
get_local $i
|
||||
i32.load8_u
|
||||
get_local $j
|
||||
i32.load8_u
|
||||
i32.ne
|
||||
br_if $breakMatchChars
|
||||
;; j += 1;
|
||||
get_local $j
|
||||
i32.const 1
|
||||
i32.add
|
||||
tee_local $j
|
||||
;; if ( j === needleRight ) { return haystackLeft; }
|
||||
get_local $needleRight
|
||||
i32.eq
|
||||
if
|
||||
get_local $haystackLeft
|
||||
return
|
||||
end
|
||||
;; i += 1;
|
||||
get_local $i
|
||||
i32.const 1
|
||||
i32.add
|
||||
set_local $i
|
||||
br $matchChars
|
||||
;; }
|
||||
end end
|
||||
;; haystackLeft += 1;
|
||||
get_local $haystackLeft
|
||||
i32.const 1
|
||||
i32.add
|
||||
tee_local $haystackLeft
|
||||
;; if ( haystackLeft === haystackEnd ) { break; }
|
||||
get_local $haystackEnd
|
||||
i32.ne
|
||||
br_if $mainLoop
|
||||
;; }
|
||||
end end
|
||||
;; return -1;
|
||||
i32.const -1
|
||||
)
|
||||
|
||||
;;
|
||||
;; int lastIndexOf(haystackBeg, haystackEnd, needleLeft, needleLen)
|
||||
;;
|
||||
;; Test whether the string at needleOffset and of length needleLen is found in
|
||||
;; the haystack at or to the right of haystackBeg, but not farther than
|
||||
;; haystackEnd.
|
||||
;;
|
||||
(func (export "lastIndexOf")
|
||||
(param $haystackBeg i32) ;; start offset in haystack
|
||||
(param $haystackEnd i32) ;; end offset in haystack
|
||||
(param $needleLeft i32) ;; start of needle in character buffer
|
||||
(param $needleLen i32) ;; number of characters to match
|
||||
(result i32) ;; result: index of match, -1 = no match
|
||||
(local $haystackLeft i32)
|
||||
(local $needleRight i32)
|
||||
(local $i i32)
|
||||
(local $j i32)
|
||||
(local $c0 i32)
|
||||
;; let haystackLeft = haystackEnd - needleLen;
|
||||
get_local $haystackEnd
|
||||
get_local $needleLen
|
||||
i32.sub
|
||||
tee_local $haystackLeft
|
||||
;; if ( haystackLeft < haystackBeg ) { return -1; }
|
||||
get_local $haystackBeg
|
||||
i32.lt_s
|
||||
if
|
||||
i32.const -1
|
||||
return
|
||||
end
|
||||
;; needleLeft += this.buf32[CHAR0_SLOT];
|
||||
get_local $needleLeft
|
||||
i32.const 2060 ;; CHAR0_SLOT memory address
|
||||
i32.load ;; CHAR0 memory address
|
||||
i32.add ;; needle memory address
|
||||
tee_local $needleLeft
|
||||
;; const needleRight = needleLeft + needleLen;
|
||||
get_local $needleLen
|
||||
i32.add
|
||||
set_local $needleRight
|
||||
;; const charCodes = this.buf8;
|
||||
;; for (;;) {
|
||||
block $breakMainLoop loop $mainLoop
|
||||
;; let i = haystackLeft;
|
||||
;; let j = needleLeft;
|
||||
get_local $haystackLeft
|
||||
set_local $i
|
||||
get_local $needleLeft
|
||||
set_local $j
|
||||
;; while ( charCodes[i] === charCodes[j] ) {
|
||||
block $breakMatchChars loop $matchChars
|
||||
get_local $i
|
||||
i32.load8_u
|
||||
get_local $j
|
||||
i32.load8_u
|
||||
i32.ne
|
||||
br_if $breakMatchChars
|
||||
;; j += 1;
|
||||
get_local $j
|
||||
i32.const 1
|
||||
i32.add
|
||||
tee_local $j
|
||||
;; if ( j === needleRight ) { return haystackLeft; }
|
||||
get_local $needleRight
|
||||
i32.eq
|
||||
if
|
||||
get_local $haystackLeft
|
||||
return
|
||||
end
|
||||
;; i += 1;
|
||||
get_local $i
|
||||
i32.const 1
|
||||
i32.add
|
||||
set_local $i
|
||||
br $matchChars
|
||||
;; }
|
||||
end end
|
||||
;; if ( haystackLeft === haystackBeg ) { break; }
|
||||
;; haystackLeft -= 1;
|
||||
get_local $haystackLeft
|
||||
get_local $haystackBeg
|
||||
i32.eq
|
||||
br_if $breakMainLoop
|
||||
get_local $haystackLeft
|
||||
i32.const 1
|
||||
i32.sub
|
||||
set_local $haystackLeft
|
||||
br $mainLoop
|
||||
;; }
|
||||
end end
|
||||
;; return -1;
|
||||
i32.const -1
|
||||
)
|
||||
|
||||
;;
|
||||
;; module end
|
||||
;;
|
||||
)
|
Loading…
Reference in New Issue
Block a user