diff options
Diffstat (limited to 'src/js/wasm')
-rw-r--r-- | src/js/wasm/README.md | 24 | ||||
-rw-r--r-- | src/js/wasm/biditrie.wasm | bin | 0 -> 990 bytes | |||
-rw-r--r-- | src/js/wasm/biditrie.wat | 728 | ||||
-rw-r--r-- | src/js/wasm/hntrie.wasm | bin | 0 -> 1034 bytes | |||
-rw-r--r-- | src/js/wasm/hntrie.wat | 724 |
5 files changed, 1476 insertions, 0 deletions
diff --git a/src/js/wasm/README.md b/src/js/wasm/README.md new file mode 100644 index 0000000..32aef07 --- /dev/null +++ b/src/js/wasm/README.md @@ -0,0 +1,24 @@ +### For code reviewers + +All `wasm` files in that directory where created by compiling the +corresponding `wat` file using the command (using `hntrie.wat`/`hntrie.wasm` +as example): + + wat2wasm hntrie.wat -o hntrie.wasm + +Assuming: + +- The command is executed from within the present directory. + +### `wat2wasm` tool + +The `wat2wasm` tool can be downloaded from an official WebAssembly project: +<https://github.com/WebAssembly/wabt/releases>. + +### `wat2wasm` tool online + +You can also use the following online `wat2wasm` tool: +<https://webassembly.github.io/wabt/demo/wat2wasm/>. + +Just paste the whole content of the `wat` file to compile into the WAT pane. +Click "Download" button to retrieve the resulting `wasm` file.
\ No newline at end of file diff --git a/src/js/wasm/biditrie.wasm b/src/js/wasm/biditrie.wasm Binary files differnew file mode 100644 index 0000000..5bfc6b7 --- /dev/null +++ b/src/js/wasm/biditrie.wasm diff --git a/src/js/wasm/biditrie.wat b/src/js/wasm/biditrie.wat new file mode 100644 index 0000000..a6c80ba --- /dev/null +++ b/src/js/wasm/biditrie.wat @@ -0,0 +1,728 @@ +;; +;; uBlock Origin - a comprehensive, efficient content blocker +;; 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) +(func $extraHandler (import "imports" "extraHandler") (param i32 i32 i32) (result i32)) + +;; Trie container +;; +;; Memory layout, byte offset: +;; const HAYSTACK_START = 0; +;; const HAYSTACK_SIZE = 2048; // i32 / i8 +;; const HAYSTACK_SIZE_SLOT = HAYSTACK_SIZE >>> 2; // 512 / 2048 +;; const TRIE0_SLOT = HAYSTACK_SIZE_SLOT + 1; // 513 / 2052 +;; const TRIE1_SLOT = HAYSTACK_SIZE_SLOT + 2; // 514 / 2056 +;; const CHAR0_SLOT = HAYSTACK_SIZE_SLOT + 3; // 515 / 2060 +;; const CHAR1_SLOT = HAYSTACK_SIZE_SLOT + 4; // 516 / 2064 +;; const RESULT_L_SLOT = HAYSTACK_SIZE_SLOT + 5; // 517 / 2068 +;; const RESULT_R_SLOT = HAYSTACK_SIZE_SLOT + 6; // 518 / 2072 +;; const RESULT_IU_SLOT = HAYSTACK_SIZE_SLOT + 7; // 519 / 2076 +;; const TRIE0_START = HAYSTACK_SIZE_SLOT + 8 << 2; // 2080 +;; + +;; +;; Public functions +;; + +;; +;; unsigned int matches(icell, ai) +;; +;; Test whether the trie at icell matches the haystack content at position ai. +;; +(func (export "matches") + (param $icell i32) ;; start offset in haystack + (param $ai i32) ;; offset in haystack + (result i32) ;; result: 0 = no match, 1 = match + (local $char0 i32) + (local $aR i32) + (local $al i32) + (local $bl i32) + (local $x i32) + (local $y i32) + ;; trie index is a uint32 offset, need to convert to uint8 offset + local.get $icell + i32.const 2 + i32.shl + local.set $icell + ;; const buf32 = this.buf32; + ;; const buf8 = this.buf8; + ;; const char0 = buf32[CHAR0_SLOT]; + i32.const 2060 + i32.load align=4 + local.set $char0 + ;; const aR = buf32[HAYSTACK_SIZE_SLOT]; + i32.const 2048 + i32.load align=4 + local.set $aR + ;; let al = ai; + local.get $ai + local.set $al + block $matchFound + block $matchNotFound + ;; for (;;) { + loop $mainLoop + ;; x = buf8[al]; + local.get $al + i32.load8_u + local.set $x + ;; al += 1; + local.get $al + i32.const 1 + i32.add + local.set $al + ;; // find matching segment + ;; for (;;) { + block $nextSegment loop $findSegment + ;; y = buf32[icell+SEGMENT_INFO]; + local.get $icell + i32.load offset=8 align=4 + local.tee $y + ;; bl = char0 + (y & 0x00FFFFFF); + i32.const 0x00FFFFFF + i32.and + local.get $char0 + i32.add + local.tee $bl + ;; if ( buf8[bl] === x ) { + i32.load8_u + local.get $x + i32.eq + if + ;; y = (y >>> 24) - 1; + local.get $y + i32.const 24 + i32.shr_u + i32.const 1 + i32.sub + local.tee $y + ;; if ( n !== 0 ) { + if + ;; x = al + y; + local.get $y + local.get $al + i32.add + local.tee $x + ;; if ( x > aR ) { return 0; } + local.get $aR + i32.gt_u + br_if $matchNotFound + ;; for (;;) { + loop + ;; bl += 1; + local.get $bl + i32.const 1 + i32.add + local.tee $bl + ;; if ( buf8[bl] !== buf8[al] ) { return 0; } + i32.load8_u + local.get $al + i32.load8_u + i32.ne + br_if $matchNotFound + ;; al += 1; + local.get $al + i32.const 1 + i32.add + local.tee $al + ;; if ( al === x ) { break; } + local.get $x + i32.ne + br_if 0 + end + ;; } + end + br $nextSegment + end + ;; icell = buf32[icell+CELL_OR]; + local.get $icell + i32.load offset=4 align=4 + i32.const 2 + i32.shl + local.tee $icell + ;; if ( icell === 0 ) { return 0; } + i32.eqz + br_if $matchNotFound + br $findSegment + ;; } + end end + ;; // next segment + ;; icell = buf32[icell+CELL_AND]; + local.get $icell + i32.load align=4 + i32.const 2 + i32.shl + local.tee $icell + ;; const x = buf32[icell+BCELL_EXTRA]; + i32.load offset=8 align=4 + local.tee $x + ;; if ( x <= BCELL_EXTRA_MAX ) { + i32.const 0x00FFFFFF + i32.le_u + if + ;; if ( x !== 0 && this.matchesExtra(ai, al, x) !== 0 ) { + ;; return 1; + ;; } + local.get $x + if + local.get $ai + local.get $al + local.get $x + call $matchesExtra + br_if $matchFound + end + ;; x = buf32[icell+BCELL_ALT_AND]; + local.get $icell + i32.load offset=4 align=4 + i32.const 2 + i32.shl + local.tee $x + ;; if ( x !== 0 && this.matchesLeft(x, ai, al) !== 0 ) { + if + local.get $x + local.get $ai + local.get $al + call $matchesLeft + br_if $matchFound + ;; } + end + ;; icell = buf32[icell+BCELL_NEXT_AND]; + local.get $icell + i32.load align=4 + i32.const 2 + i32.shl + local.tee $icell + ;; if ( icell === 0 ) { return 0; } + i32.eqz + br_if $matchNotFound + ;; } + end + ;; if ( al === aR ) { return 0; } + local.get $al + local.get $aR + i32.ne + br_if $mainLoop + ;; } + end ;; $mainLoop + end ;; $matchNotFound + i32.const 0 + return + end ;; $matchFound + i32.const 1 + return +) + +;; +;; unsigned int matchesLeft(icell, ar, r) +;; +;; Test whether the trie at icell matches the haystack content at position ai. +;; +(func $matchesLeft + (param $icell i32) ;; start offset in haystack + (param $ar i32) ;; offset of where to start in haystack + (param $r i32) ;; right bound of match so far + (result i32) ;; result: 0 = no match, 1 = match + (local $char0 i32) + (local $bl i32) + (local $br i32) + (local $x i32) + (local $y i32) + ;; const buf32 = this.buf32; + ;; const buf8 = this.buf8; + ;; const char0 = buf32[CHAR0_SLOT]; + i32.const 2060 + i32.load align=4 + local.set $char0 + block $matchFound + block $matchNotFound + ;; for (;;) { + loop $mainLoop + ;; if ( ar === 0 ) { return 0; } + local.get $ar + i32.eqz + br_if $matchNotFound + ;; ar -= 1; + local.get $ar + i32.const 1 + i32.sub + local.tee $ar + ;; x = buf8[ar]; + i32.load8_u + local.set $x + ;; // find matching segment + ;; for (;;) { + block $nextSegment loop $findSegment + ;; y = buf32[icell+SEGMENT_INFO]; + local.get $icell + i32.load offset=8 align=4 + local.tee $y + ;; br = char0 + (y & 0x00FFFFFF); + i32.const 0x00FFFFFF + i32.and + local.get $char0 + i32.add + local.tee $br + ;; y = (y >>> 24) - 1; + local.get $y + i32.const 24 + i32.shr_u + i32.const 1 + i32.sub + local.tee $y + ;; br += y; + i32.add + local.tee $br + ;; if ( buf8[br] === x ) { + i32.load8_u + local.get $x + i32.eq + if + ;; // all characters in segment must match + ;; if ( y !== 0 ) { + local.get $y + if + ;; x = ar - y; + local.get $ar + local.get $y + i32.sub + local.tee $x + ;; if ( x < 0 ) { return 0; } + i32.const 0 + i32.lt_s + br_if $matchNotFound + ;; for (;;) { + loop + ;; ar -= 1; br -= 1; + ;; if ( buf8[ar] !== buf8[br] ) { return 0; } + local.get $ar + i32.const 1 + i32.sub + local.tee $ar + i32.load8_u + local.get $br + i32.const 1 + i32.sub + local.tee $br + i32.load8_u + i32.ne + br_if $matchNotFound + ;; if ( ar === x ) { break; } + local.get $ar + local.get $x + i32.ne + br_if 0 + end + ;; } + end + br $nextSegment + end + ;; icell = buf32[icell+CELL_OR]; + local.get $icell + i32.load offset=4 align=4 + i32.const 2 + i32.shl + local.tee $icell + ;; if ( icell === 0 ) { return 0; } + i32.eqz + br_if $matchNotFound + br $findSegment + ;; } + end end + ;; // next segment + ;; icell = buf32[icell+CELL_AND]; + local.get $icell + i32.load align=4 + i32.const 2 + i32.shl + local.tee $icell + ;; const x = buf32[icell+BCELL_EXTRA]; + i32.load offset=8 align=4 + local.tee $x + ;; if ( x <= BCELL_EXTRA_MAX ) { + i32.const 0x00FFFFFF + i32.le_u + if + ;; if ( x !== 0 && this.matchesExtra(ar, r, x) !== 0 ) { + ;; return 1; + ;; } + local.get $x + if + local.get $ar + local.get $r + local.get $x + call $matchesExtra + br_if $matchFound + end + ;; icell = buf32[icell+BCELL_NEXT_AND]; + local.get $icell + i32.load align=4 + i32.const 2 + i32.shl + local.tee $icell + ;; if ( icell === 0 ) { return 0; } + i32.eqz + br_if $matchNotFound + ;; } + end + br $mainLoop + ;; } + end ;; $mainLoop + end ;; $matchNotFound + i32.const 0 + return + end ;; $matchFound + i32.const 1 + return +) + +;; +;; int matchExtra(l, r, ix) +;; +;; Test whether extra handler returns a match. +;; +(func $matchesExtra + (param $l i32) ;; left bound of match so far + (param $r i32) ;; right bound of match so far + (param $ix i32) ;; extra token + (result i32) ;; result: 0 = no match, 1 = match + (local $iu i32) ;; filter unit + block $fail + block $succeed + ;; if ( ix !== 1 ) { + ;; const iu = this.extraHandler(l, r, ix); + ;; if ( iu === 0 ) { return 0; } + local.get $ix + i32.const 1 + i32.ne + if + local.get $l + local.get $r + local.get $ix + call $extraHandler + local.tee $iu + i32.eqz + br_if $fail + ;; } else { + ;; iu = -1; + else + i32.const -1 + local.set $iu + ;; } + end + ;; this.buf32[RESULT_IU_SLOT] = iu; + i32.const 2076 + local.get $iu + i32.store align=4 + ;; this.buf32[RESULT_L_SLOT] = l; + i32.const 2068 + local.get $l + i32.store align=4 + ;; this.buf32[RESULT_R_SLOT] = r; + i32.const 2072 + local.get $r + i32.store align=4 + end ;; $succeed + i32.const 1 + return + end ;; $fail + i32.const 0 +) + +;; +;; 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) + block $fail + block $succeed + ;; + ;; if ( haystackLeft < 0 || (haystackLeft + needleLen) > haystackRight ) { + ;; return 0; + ;; } + local.get $haystackLeft + i32.const 0 + i32.lt_s + br_if $fail + local.get $haystackLeft + local.get $needleLen + i32.add + local.get $haystackRight + i32.gt_u + br_if $fail + ;; const charCodes = this.buf8; + ;; needleLeft += this.buf32[CHAR0_SLOT]; + local.get $needleLeft + i32.const 2060 ;; CHAR0_SLOT memory address + i32.load align=4 ;; CHAR0 memory address + i32.add ;; needle memory address + local.tee $needleLeft + ;; const needleRight = needleLeft + needleLen; + local.get $needleLen + i32.add + local.set $needleRight + ;; while ( charCodes[haystackLeft] === charCodes[needleLeft] ) { + loop $compare + local.get $haystackLeft + i32.load8_u + local.get $needleLeft + i32.load8_u + i32.ne + br_if $fail + ;; needleLeft += 1; + local.get $needleLeft + i32.const 1 + i32.add + local.tee $needleLeft + ;; if ( needleLeft === needleRight ) { return 1; } + local.get $needleRight + i32.eq + br_if $succeed + ;; haystackLeft += 1; + i32.const 1 + local.get $haystackLeft + i32.add + local.set $haystackLeft + br $compare + end + ;; } + ;; return 1; + end ;; $succeed + i32.const 1 + return + ;; return 0; + end ;; $fail + i32.const 0 +) + +;; +;; 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) + block $fail + block $succeed + ;; if ( needleLen === 0 ) { return haystackLeft; } + local.get $needleLen + i32.eqz + br_if $succeed + ;; haystackEnd -= needleLen; + local.get $haystackEnd + local.get $needleLen + i32.sub + local.tee $haystackEnd + ;; if ( haystackEnd < haystackLeft ) { return -1; } + local.get $haystackLeft + i32.lt_s + br_if $fail + ;; needleLeft += this.buf32[CHAR0_SLOT]; + local.get $needleLeft + i32.const 2060 ;; CHAR0_SLOT memory address + i32.load align=4 ;; CHAR0 memory address + i32.add ;; needle memory address + local.tee $needleLeft + ;; const needleRight = needleLeft + needleLen; + local.get $needleLen + i32.add + local.set $needleRight + ;; const charCodes = this.buf8; + ;; for (;;) { + loop $mainLoop + ;; let i = haystackLeft; + ;; let j = needleLeft; + local.get $haystackLeft + local.set $i + local.get $needleLeft + local.set $j + ;; while ( charCodes[i] === charCodes[j] ) { + block $breakMatchChars loop $matchChars + local.get $i + i32.load8_u + local.get $j + i32.load8_u + i32.ne + br_if $breakMatchChars + ;; j += 1; + local.get $j + i32.const 1 + i32.add + local.tee $j + ;; if ( j === needleRight ) { return haystackLeft; } + local.get $needleRight + i32.eq + br_if $succeed + ;; i += 1; + local.get $i + i32.const 1 + i32.add + local.set $i + br $matchChars + ;; } + end end + ;; haystackLeft += 1; + local.get $haystackLeft + i32.const 1 + i32.add + local.tee $haystackLeft + ;; if ( haystackLeft > haystackEnd ) { break; } + local.get $haystackEnd + i32.gt_u + br_if $fail + br $mainLoop + ;; } + end + end ;; $succeed + local.get $haystackLeft + return + end ;; $fail + ;; 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) + ;; if ( needleLen === 0 ) { return haystackBeg; } + local.get $needleLen + i32.eqz + if + local.get $haystackBeg + return + end + block $fail + block $succeed + ;; let haystackLeft = haystackEnd - needleLen; + local.get $haystackEnd + local.get $needleLen + i32.sub + local.tee $haystackLeft + ;; if ( haystackLeft < haystackBeg ) { return -1; } + local.get $haystackBeg + i32.lt_s + br_if $fail + ;; needleLeft += this.buf32[CHAR0_SLOT]; + local.get $needleLeft + i32.const 2060 ;; CHAR0_SLOT memory address + i32.load align=4 ;; CHAR0 memory address + i32.add ;; needle memory address + local.tee $needleLeft + ;; const needleRight = needleLeft + needleLen; + local.get $needleLen + i32.add + local.set $needleRight + ;; const charCodes = this.buf8; + ;; for (;;) { + loop $mainLoop + ;; let i = haystackLeft; + ;; let j = needleLeft; + local.get $haystackLeft + local.set $i + local.get $needleLeft + local.set $j + ;; while ( charCodes[i] === charCodes[j] ) { + block $breakMatchChars loop $matchChars + local.get $i + i32.load8_u + local.get $j + i32.load8_u + i32.ne + br_if $breakMatchChars + ;; j += 1; + local.get $j + i32.const 1 + i32.add + local.tee $j + ;; if ( j === needleRight ) { return haystackLeft; } + local.get $needleRight + i32.eq + br_if $succeed + ;; i += 1; + local.get $i + i32.const 1 + i32.add + local.set $i + br $matchChars + ;; } + end end + ;; if ( haystackLeft === haystackBeg ) { break; } + ;; haystackLeft -= 1; + local.get $haystackLeft + local.get $haystackBeg + i32.eq + br_if $fail + local.get $haystackLeft + i32.const 1 + i32.sub + local.set $haystackLeft + br $mainLoop + ;; } + end + end ;; $succeed + local.get $haystackLeft + return + end ;; $fail + ;; return -1; + i32.const -1 +) + +;; +;; module end +;; +) diff --git a/src/js/wasm/hntrie.wasm b/src/js/wasm/hntrie.wasm Binary files differnew file mode 100644 index 0000000..9067f42 --- /dev/null +++ b/src/js/wasm/hntrie.wasm diff --git a/src/js/wasm/hntrie.wat b/src/js/wasm/hntrie.wat new file mode 100644 index 0000000..666c44e --- /dev/null +++ b/src/js/wasm/hntrie.wat @@ -0,0 +1,724 @@ +;; +;; uBlock Origin - a comprehensive, efficient content blocker +;; Copyright (C) 2018-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: hntrie.wat +;; Description: WebAssembly code used by src/js/hntrie.js +;; How to compile: See README.md in this directory. + +(module +;; +;; module start +;; + +(func $growBuf (import "imports" "growBuf")) +(memory (import "imports" "memory") 1) + +;; Trie container +;; +;; Memory layout, byte offset: +;; 0-254: needle being processed +;; 255: length of needle +;; 256-259: offset to start of trie data section (=> trie0) +;; 260-263: offset to end of trie data section (=> trie1) +;; 264-267: offset to start of character data section (=> char0) +;; 268-271: offset to end of character data section (=> char1) +;; 272: start of trie data section +;; + +;; +;; Public functions +;; + +;; +;; unsigned int matches(icell) +;; +;; Test whether the currently set needle matches the trie at specified trie +;; offset. +;; +(func (export "matches") + (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) + (local $v i32) + (local $n i32) + (local $i0 i32) + (local $i1 i32) + ;; + i32.const 264 ;; start of char section is stored at addr 264 + i32.load + local.set $char0 + ;; let ineedle = this.buf[255]; + i32.const 255 ;; addr of needle is stored at addr 255 + i32.load8_u + local.set $ineedle + ;; let icell = this.buf32[iroot+0]; + local.get $iroot + i32.const 2 + i32.shl + i32.load + i32.const 2 + i32.shl + local.tee $icell + ;; if ( icell === 0 ) { return -1; } + i32.eqz + if + i32.const -1 + return + end + ;; for (;;) { + block $noSegment loop $nextSegment + ;; if ( ineedle === 0 ) { return -1; } + local.get $ineedle + i32.eqz + if + i32.const -1 + return + end + ;; ineedle -= 1; + local.get $ineedle + i32.const -1 + i32.add + local.tee $ineedle + ;; let c = this.buf[ineedle]; + i32.load8_u + local.set $c + ;; for (;;) { + block $foundSegment loop $findSegment + ;; v = this.buf32[icell+2]; + local.get $icell + i32.load offset=8 + local.tee $v + ;; i0 = char0 + (v >>> 8); + i32.const 8 + i32.shr_u + local.get $char0 + i32.add + local.tee $i0 + ;; if ( this.buf[i0] === c ) { break; } + i32.load8_u + local.get $c + i32.eq + br_if $foundSegment + ;; icell = this.buf32[icell+0]; + local.get $icell + i32.load + i32.const 2 + i32.shl + local.tee $icell + i32.eqz + if + i32.const -1 + return + end + br 0 + end end + ;; let n = v & 0x7F; + local.get $v + i32.const 0x7F + i32.and + local.tee $n + ;; if ( n > 1 ) { + i32.const 1 + i32.gt_u + if + ;; n -= 1; + local.get $n + i32.const -1 + i32.add + local.tee $n + ;; if ( n > ineedle ) { return -1; } + local.get $ineedle + i32.gt_u + if + i32.const -1 + return + end + local.get $i0 + i32.const 1 + i32.add + local.tee $i0 + ;; const i1 = i0 + n; + local.get $n + i32.add + local.set $i1 + ;; do { + loop + ;; ineedle -= 1; + local.get $ineedle + i32.const -1 + i32.add + local.tee $ineedle + ;; if ( this.buf[i0] !== this.buf[ineedle] ) { return -1; } + i32.load8_u + local.get $i0 + i32.load8_u + i32.ne + if + i32.const -1 + return + end + ;; i0 += 1; + local.get $i0 + i32.const 1 + i32.add + local.tee $i0 + ;; } while ( i0 < i1 ); + local.get $i1 + i32.lt_u + br_if 0 + end + end + ;; if ( (v & 0x80) !== 0 ) { + local.get $v + i32.const 0x80 + i32.and + if + ;; if ( ineedle === 0 || buf8[ineedle-1] === 0x2E /* '.' */ ) { + ;; return ineedle; + ;; } + local.get $ineedle + i32.eqz + if + i32.const 0 + return + end + local.get $ineedle + i32.const -1 + i32.add + i32.load8_u + i32.const 0x2E + i32.eq + if + local.get $ineedle + return + end + end + ;; icell = this.buf32[icell+1]; + local.get $icell + i32.load offset=4 + i32.const 2 + i32.shl + local.tee $icell + ;; if ( icell === 0 ) { break; } + br_if 0 + end end + ;; return -1; + i32.const -1 +) + +;; +;; unsigned int add(icell) +;; +;; Add a new hostname to a trie which root cell is passed as argument. +;; +(func (export "add") + (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 $v i32) ;; integer value describing a segment + (local $isegchar0 i32) ;; offset to start of current segment's character data + (local $isegchar i32) + (local $lsegchar i32) ;; number of character in current segment + (local $inext i32) ;; index of next cell to process + (local $boundaryBit i32) ;; the boundary bit state of the current cell + ;; + ;; let lhnchar = this.buf[255]; + i32.const 255 + i32.load8_u + local.tee $lhnchar + ;; if ( lhnchar === 0 ) { return 0; } + i32.eqz + if + i32.const 0 + return + end + ;; if ( + ;; (this.buf32[HNBIGTRIE_CHAR0_SLOT] - this.buf32[HNBIGTRIE_TRIE1_SLOT]) < 24 || + ;; (this.buf.length - this.buf32[HNBIGTRIE_CHAR1_SLOT]) < 256 + ;; ) { + ;; this.growBuf(); + ;; } + i32.const 264 + i32.load + i32.const 260 + i32.load + i32.sub + i32.const 24 + i32.lt_u + if + call $growBuf + else + memory.size + i32.const 16 + i32.shl + i32.const 268 + i32.load + i32.sub + i32.const 256 + i32.lt_u + if + call $growBuf + end + end + ;; let icell = this.buf32[iroot+0]; + local.get $iroot + i32.const 2 + i32.shl + local.tee $iroot + i32.load + i32.const 2 + i32.shl + local.tee $icell + ;; if ( this.buf32[icell+2] === 0 ) { + i32.eqz + if + ;; this.buf32[iroot+0] = this.addLeafCell(lhnchar); + ;; return 1; + local.get $iroot + local.get $lhnchar + call $addLeafCell + i32.store + i32.const 1 + return + end + ;; const char0 = this.buf32[HNBIGTRIE_CHAR0_SLOT]; + i32.const 264 + i32.load + local.set $char0 + ;; for (;;) { + loop $nextSegment + ;; const v = this.buf32[icell+2]; + local.get $icell + i32.load offset=8 + local.tee $v + ;; let isegchar0 = char0 + (v >>> 8); + i32.const 8 + i32.shr_u + local.get $char0 + i32.add + local.tee $isegchar0 + ;; if ( this.buf[isegchar0] !== this.buf[lhnchar-1] ) { + i32.load8_u + local.get $lhnchar + i32.const -1 + i32.add + i32.load8_u + i32.ne + if + ;; inext = this.buf32[icell+0]; + local.get $icell + i32.load + local.tee $inext + ;; if ( inext === 0 ) { + i32.eqz + if + ;; this.buf32[icell+0] = this.addLeafCell(lhnchar); + local.get $icell + local.get $lhnchar + call $addLeafCell + i32.store + ;; return 1; + i32.const 1 + return + end + ;; icell = inext; + local.get $inext + i32.const 2 + i32.shl + local.set $icell + br $nextSegment + end + ;; let isegchar = 1; + i32.const 1 + local.set $isegchar + ;; lhnchar -= 1; + local.get $lhnchar + i32.const -1 + i32.add + local.set $lhnchar + ;; const lsegchar = v & 0x7F; + local.get $v + i32.const 0x7F + i32.and + local.tee $lsegchar + ;; if ( lsegchar !== 1 ) { + i32.const 1 + i32.ne + if + ;; for (;;) { + block $mismatch loop + ;; if ( isegchar === lsegchar ) { break; } + local.get $isegchar + local.get $lsegchar + i32.eq + br_if $mismatch + local.get $lhnchar + i32.eqz + br_if $mismatch + ;; if ( this.buf[isegchar0+isegchar] !== this.buf[lhnchar-1] ) { break; } + local.get $isegchar0 + local.get $isegchar + i32.add + i32.load8_u + local.get $lhnchar + i32.const -1 + i32.add + i32.load8_u + i32.ne + br_if $mismatch + ;; isegchar += 1; + local.get $isegchar + i32.const 1 + i32.add + local.set $isegchar + ;; lhnchar -= 1; + local.get $lhnchar + i32.const -1 + i32.add + local.set $lhnchar + br 0 + end end + end + ;; const boundaryBit = v & 0x80; + local.get $v + i32.const 0x80 + i32.and + local.set $boundaryBit + ;; if ( isegchar === lsegchar ) { + local.get $isegchar + local.get $lsegchar + i32.eq + if + ;; if ( lhnchar === 0 ) { + local.get $lhnchar + i32.eqz + if + ;; if ( boundaryBit !== 0 ) { return 0; } + local.get $boundaryBit + if + i32.const 0 + return + end + ;; this.buf32[icell+2] = v | 0x80; + local.get $icell + local.get $v + i32.const 0x80 + i32.or + i32.store offset=8 + else + ;; if ( boundaryBit !== 0 ) { + local.get $boundaryBit + if + ;; if ( this.buf[lhnchar-1] === 0x2E /* '.' */ ) { return -1; } + local.get $lhnchar + i32.const -1 + i32.add + i32.load8_u + i32.const 0x2E + i32.eq + if + i32.const -1 + return + end + end + ;; inext = this.buf32[icell+1]; + local.get $icell + i32.load offset=4 + local.tee $inext + ;; if ( inext !== 0 ) { + if + ;; icell = inext; + local.get $inext + i32.const 2 + i32.shl + local.set $icell + ;; continue; + br $nextSegment + end + ;; this.buf32[icell+1] = this.addLeafCell(lhnchar); + local.get $icell + local.get $lhnchar + call $addLeafCell + i32.store offset=4 + end + else + ;; isegchar0 -= char0; + local.get $icell + local.get $isegchar0 + local.get $char0 + i32.sub + local.tee $isegchar0 + ;; this.buf32[icell+2] = isegchar0 << 8 | isegchar; + i32.const 8 + i32.shl + local.get $isegchar + i32.or + i32.store offset=8 + ;; inext = this.addCell( + ;; 0, + ;; this.buf32[icell+1], + ;; isegchar0 + isegchar << 8 | boundaryBit | lsegchar - isegchar + ;; ); + local.get $icell + i32.const 0 + local.get $icell + i32.load offset=4 + local.get $isegchar0 + local.get $isegchar + i32.add + i32.const 8 + i32.shl + local.get $boundaryBit + i32.or + local.get $lsegchar + local.get $isegchar + i32.sub + i32.or + call $addCell + local.tee $inext + ;; this.buf32[icell+1] = inext; + i32.store offset=4 + ;; if ( lhnchar !== 0 ) { + local.get $lhnchar + if + ;; this.buf32[inext+0] = this.addLeafCell(lhnchar); + local.get $inext + i32.const 2 + i32.shl + local.get $lhnchar + call $addLeafCell + i32.store + else + ;; this.buf32[icell+2] |= 0x80; + local.get $icell + local.get $icell + i32.load offset=8 + i32.const 0x80 + i32.or + i32.store offset=8 + end + end + ;; return 1; + i32.const 1 + return + end + ;; + i32.const 1 +) + +;; +;; Private functions +;; + +;; +;; unsigned int addCell(idown, iright, v) +;; +;; Add a new cell, return cell index. +;; +(func $addCell + (param $idown i32) + (param $iright i32) + (param $v i32) + (result i32) ;; result: index of added cell + (local $icell i32) + ;; + ;; let icell = this.buf32[HNBIGTRIE_TRIE1_SLOT]; + ;; this.buf32[HNBIGTRIE_TRIE1_SLOT] = icell + 12; + i32.const 260 + i32.const 260 + i32.load + local.tee $icell + i32.const 12 + i32.add + i32.store + ;; this.buf32[icell+0] = idown; + local.get $icell + local.get $idown + i32.store + ;; this.buf32[icell+1] = iright; + local.get $icell + local.get $iright + i32.store offset=4 + ;; this.buf32[icell+2] = v; + local.get $icell + local.get $v + i32.store offset=8 + ;; return icell; + local.get $icell + i32.const 2 + i32.shr_u +) + +;; +;; unsigned int addLeafCell(lsegchar) +;; +;; Add a new cell, return cell index. +;; +(func $addLeafCell + (param $lsegchar i32) + (result i32) ;; result: index of added cell + (local $r i32) + (local $i i32) + ;; const r = this.buf32[TRIE1_SLOT] >>> 2; + i32.const 260 + i32.load + local.tee $r + ;; let i = r; + local.set $i + ;; while ( lsegchar > 127 ) { + block $lastSegment loop + local.get $lsegchar + i32.const 127 + i32.le_u + br_if $lastSegment + ;; this.buf32[i+0] = 0; + local.get $i + i32.const 0 + i32.store + ;; this.buf32[i+1] = i + 3; + local.get $i + local.get $i + i32.const 12 + i32.add + i32.const 2 + i32.shr_u + i32.store offset=4 + ;; this.buf32[i+2] = this.addSegment(lsegchar, lsegchar - 127); + local.get $i + local.get $lsegchar + local.get $lsegchar + i32.const 127 + i32.sub + call $addSegment + i32.store offset=8 + ;; lsegchar -= 127; + local.get $lsegchar + i32.const 127 + i32.sub + local.set $lsegchar + ;; i += 3; + local.get $i + i32.const 12 + i32.add + local.set $i + br 0 + end end + ;; this.buf32[i+0] = 0; + local.get $i + i32.const 0 + i32.store + ;; this.buf32[i+1] = 0; + local.get $i + i32.const 0 + i32.store offset=4 + ;; this.buf32[i+2] = this.addSegment(lsegchar, 0) | 0x80; + local.get $i + local.get $lsegchar + i32.const 0 + call $addSegment + i32.const 0x80 + i32.or + i32.store offset=8 + ;; this.buf32[TRIE1_SLOT] = i + 3 << 2; + i32.const 260 + local.get $i + i32.const 12 + i32.add + i32.store + ;; return r; + local.get $r + i32.const 2 + i32.shr_u +) + +;; +;; unsigned int addSegment(lsegchar, lsegend) +;; +;; Store a segment of characters and return a segment descriptor. The segment +;; is created from the character data in the needle buffer. +;; +(func $addSegment + (param $lsegchar i32) + (param $lsegend i32) + (result i32) ;; result: segment descriptor + (local $char1 i32) ;; offset to end of character data section + (local $isegchar i32) ;; relative offset to first character of segment + (local $i i32) ;; iterator + ;; + ;; if ( lsegchar === 0 ) { return 0; } + local.get $lsegchar + i32.eqz + if + i32.const 0 + return + end + ;; let char1 = this.buf32[HNBIGTRIE_CHAR1_SLOT]; + i32.const 268 + i32.load + local.tee $char1 + ;; const isegchar = char1 - this.buf32[HNBIGTRIE_CHAR0_SLOT]; + i32.const 264 + i32.load + i32.sub + local.set $isegchar + ;; let i = lsegchar; + local.get $lsegchar + local.set $i + ;; do { + loop + ;; this.buf[char1++] = this.buf[--i]; + local.get $char1 + local.get $i + i32.const -1 + i32.add + local.tee $i + i32.load8_u + i32.store8 + local.get $char1 + i32.const 1 + i32.add + local.set $char1 + ;; } while ( i !== lsegend ); + local.get $i + local.get $lsegend + i32.ne + br_if 0 + end + ;; this.buf32[HNBIGTRIE_CHAR1_SLOT] = char1; + i32.const 268 + local.get $char1 + i32.store + ;; return isegchar << 8 | lsegchar - lsegend; + local.get $isegchar + i32.const 8 + i32.shl + local.get $lsegchar + local.get $lsegend + i32.sub + i32.or +) + +;; +;; module end +;; +) |