1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-10-06 09:37:12 +02:00
uBlock/src/js/wasm/hntrie.wat

725 lines
19 KiB
Plaintext

;;
;; 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
;;
)