summaryrefslogtreecommitdiff
path: root/data/extensions/uBlock0@raymondhill.net/js/wasm/biditrie.wat
diff options
context:
space:
mode:
Diffstat (limited to 'data/extensions/uBlock0@raymondhill.net/js/wasm/biditrie.wat')
-rw-r--r--data/extensions/uBlock0@raymondhill.net/js/wasm/biditrie.wat728
1 files changed, 728 insertions, 0 deletions
diff --git a/data/extensions/uBlock0@raymondhill.net/js/wasm/biditrie.wat b/data/extensions/uBlock0@raymondhill.net/js/wasm/biditrie.wat
new file mode 100644
index 0000000..7a77500
--- /dev/null
+++ b/data/extensions/uBlock0@raymondhill.net/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 = 8192; // i32 / i8
+;; const HAYSTACK_SIZE_SLOT = HAYSTACK_SIZE >>> 2; // 2048 / 8192
+;; const TRIE0_SLOT = HAYSTACK_SIZE_SLOT + 1; // 2049 / 8196
+;; const TRIE1_SLOT = HAYSTACK_SIZE_SLOT + 2; // 2050 / 8200
+;; const CHAR0_SLOT = HAYSTACK_SIZE_SLOT + 3; // 2051 / 8204
+;; const CHAR1_SLOT = HAYSTACK_SIZE_SLOT + 4; // 2052 / 8208
+;; const RESULT_L_SLOT = HAYSTACK_SIZE_SLOT + 5; // 2053 / 8212
+;; const RESULT_R_SLOT = HAYSTACK_SIZE_SLOT + 6; // 2054 / 8216
+;; const RESULT_IU_SLOT = HAYSTACK_SIZE_SLOT + 7; // 2055 / 8220
+;; const TRIE0_START = HAYSTACK_SIZE_SLOT + 8 << 2; // 8224
+;;
+
+;;
+;; 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 8204
+ i32.load align=4
+ local.set $char0
+ ;; const aR = buf32[HAYSTACK_SIZE_SLOT];
+ i32.const 8192
+ 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 8204
+ 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 8220
+ local.get $iu
+ i32.store align=4
+ ;; this.buf32[RESULT_L_SLOT] = l;
+ i32.const 8212
+ local.get $l
+ i32.store align=4
+ ;; this.buf32[RESULT_R_SLOT] = r;
+ i32.const 8216
+ 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 8204 ;; 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 8204 ;; 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 8204 ;; 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
+;;
+)