Big Change Update

This commit is contained in:
yeongpin
2025-01-14 14:47:41 +08:00
parent 380ea0b81d
commit 19fe4c85f8
651 changed files with 366654 additions and 17 deletions

View File

@@ -0,0 +1,637 @@
/*******************************************************************************
publicsuffixlist.js - an efficient javascript implementation to deal with
Mozilla Foundation's Public Suffix List <http://publicsuffix.org/list/>
Copyright (C) 2013-present Raymond Hill
License: pick the one which suits you:
GPL v3 see <https://www.gnu.org/licenses/gpl.html>
APL v2 see <http://www.apache.org/licenses/LICENSE-2.0>
*/
/*! Home: https://github.com/gorhill/publicsuffixlist.js -- GPLv3 APLv2 */
'use strict';
/*******************************************************************************
Reference:
https://publicsuffix.org/list/
Excerpt:
> Algorithm
>
> 1. Match domain against all rules and take note of the matching ones.
> 2. If no rules match, the prevailing rule is "*".
> 3. If more than one rule matches, the prevailing rule is the one which
is an exception rule.
> 4. If there is no matching exception rule, the prevailing rule is the
one with the most labels.
> 5. If the prevailing rule is a exception rule, modify it by removing
the leftmost label.
> 6. The public suffix is the set of labels from the domain which match
the labels of the prevailing rule, using the matching algorithm above.
> 7. The registered or registrable domain is the public suffix plus one
additional label.
*/
/******************************************************************************/
export default (function() {
// >>>>>>>> start of anonymous namespace
/*******************************************************************************
Tree encoding in array buffer:
Node:
+ u8: length of char data
+ u8: flags => bit 0: is_publicsuffix, bit 1: is_exception
+ u16: length of array of children
+ u32: char data or offset to char data
+ u32: offset to array of children
= 12 bytes
More bits in flags could be used; for example:
- to distinguish private suffixes
*/
// i32 / i8
const HOSTNAME_SLOT = 0; // jshint ignore:line
const LABEL_INDICES_SLOT = 256; // -- / 256 (256/2 => 128 labels max)
const RULES_PTR_SLOT = 100; // 100 / 400 (400-256=144 => 144>128)
const SUFFIX_NOT_FOUND_SLOT = 399; // -- / 399 (safe, see above)
const CHARDATA_PTR_SLOT = 101; // 101 / 404
const EMPTY_STRING = '';
const SELFIE_MAGIC = 3;
let wasmMemory;
let pslBuffer32;
let pslBuffer8;
let pslByteLength = 0;
let hostnameArg = EMPTY_STRING;
/******************************************************************************/
const fireChangedEvent = function() {
if ( typeof window !== 'object' ) { return; }
if ( window instanceof Object === false ) { return; }
if ( window.dispatchEvent instanceof Function === false ) { return; }
if ( window.CustomEvent instanceof Function === false ) { return; }
window.dispatchEvent(new CustomEvent('publicSuffixListChanged'));
};
/******************************************************************************/
const allocateBuffers = function(byteLength) {
pslByteLength = byteLength + 3 & ~3;
if (
pslBuffer32 !== undefined &&
pslBuffer32.byteLength >= pslByteLength
) {
return;
}
if ( wasmMemory !== undefined ) {
const newPageCount = pslByteLength + 0xFFFF >>> 16;
const curPageCount = wasmMemory.buffer.byteLength >>> 16;
const delta = newPageCount - curPageCount;
if ( delta > 0 ) {
wasmMemory.grow(delta);
pslBuffer32 = new Uint32Array(wasmMemory.buffer);
pslBuffer8 = new Uint8Array(wasmMemory.buffer);
}
} else {
pslBuffer8 = new Uint8Array(pslByteLength);
pslBuffer32 = new Uint32Array(pslBuffer8.buffer);
}
hostnameArg = EMPTY_STRING;
pslBuffer8[LABEL_INDICES_SLOT] = 0;
};
/******************************************************************************/
// Parse and set a UTF-8 text-based suffix list. Format is same as found at:
// http://publicsuffix.org/list/
//
// `toAscii` is a converter from unicode to punycode. Required since the
// Public Suffix List contains unicode characters.
// Suggestion: use <https://github.com/bestiejs/punycode.js>
const parse = function(text, toAscii) {
// Use short property names for better minifying results
const rootRule = {
l: EMPTY_STRING, // l => label
f: 0, // f => flags
c: undefined // c => children
};
// Tree building
{
const compareLabels = function(a, b) {
let n = a.length;
let d = n - b.length;
if ( d !== 0 ) { return d; }
for ( let i = 0; i < n; i++ ) {
d = a.charCodeAt(i) - b.charCodeAt(i);
if ( d !== 0 ) { return d; }
}
return 0;
};
const addToTree = function(rule, exception) {
let node = rootRule;
let end = rule.length;
while ( end > 0 ) {
const beg = rule.lastIndexOf('.', end - 1);
const label = rule.slice(beg + 1, end);
end = beg;
if ( Array.isArray(node.c) === false ) {
const child = { l: label, f: 0, c: undefined };
node.c = [ child ];
node = child;
continue;
}
let left = 0;
let right = node.c.length;
while ( left < right ) {
const i = left + right >>> 1;
const d = compareLabels(label, node.c[i].l);
if ( d < 0 ) {
right = i;
if ( right === left ) {
const child = {
l: label,
f: 0,
c: undefined
};
node.c.splice(left, 0, child);
node = child;
break;
}
continue;
}
if ( d > 0 ) {
left = i + 1;
if ( left === right ) {
const child = {
l: label,
f: 0,
c: undefined
};
node.c.splice(right, 0, child);
node = child;
break;
}
continue;
}
/* d === 0 */
node = node.c[i];
break;
}
}
node.f |= 0b01;
if ( exception ) {
node.f |= 0b10;
}
};
// 2. If no rules match, the prevailing rule is "*".
addToTree('*', false);
const mustPunycode = /[^a-z0-9.-]/;
const textEnd = text.length;
let lineBeg = 0;
while ( lineBeg < textEnd ) {
let lineEnd = text.indexOf('\n', lineBeg);
if ( lineEnd === -1 ) {
lineEnd = text.indexOf('\r', lineBeg);
if ( lineEnd === -1 ) {
lineEnd = textEnd;
}
}
let line = text.slice(lineBeg, lineEnd).trim();
lineBeg = lineEnd + 1;
// Ignore comments
const pos = line.indexOf('//');
if ( pos !== -1 ) {
line = line.slice(0, pos);
}
// Ignore surrounding whitespaces
line = line.trim();
if ( line.length === 0 ) { continue; }
const exception = line.charCodeAt(0) === 0x21 /* '!' */;
if ( exception ) {
line = line.slice(1);
}
if ( mustPunycode.test(line) ) {
line = toAscii(line.toLowerCase());
}
addToTree(line, exception);
}
}
{
const labelToOffsetMap = new Map();
const treeData = [];
const charData = [];
const allocate = function(n) {
const ibuf = treeData.length;
for ( let i = 0; i < n; i++ ) {
treeData.push(0);
}
return ibuf;
};
const storeNode = function(ibuf, node) {
const nChars = node.l.length;
const nChildren = node.c !== undefined
? node.c.length
: 0;
treeData[ibuf+0] = nChildren << 16 | node.f << 8 | nChars;
// char data
if ( nChars <= 4 ) {
let v = 0;
if ( nChars > 0 ) {
v |= node.l.charCodeAt(0);
if ( nChars > 1 ) {
v |= node.l.charCodeAt(1) << 8;
if ( nChars > 2 ) {
v |= node.l.charCodeAt(2) << 16;
if ( nChars > 3 ) {
v |= node.l.charCodeAt(3) << 24;
}
}
}
}
treeData[ibuf+1] = v;
} else {
let offset = labelToOffsetMap.get(node.l);
if ( offset === undefined ) {
offset = charData.length;
for ( let i = 0; i < nChars; i++ ) {
charData.push(node.l.charCodeAt(i));
}
labelToOffsetMap.set(node.l, offset);
}
treeData[ibuf+1] = offset;
}
// child nodes
if ( Array.isArray(node.c) === false ) {
treeData[ibuf+2] = 0;
return;
}
const iarray = allocate(nChildren * 3);
treeData[ibuf+2] = iarray;
for ( let i = 0; i < nChildren; i++ ) {
storeNode(iarray + i * 3, node.c[i]);
}
};
// First 512 bytes are reserved for internal use
allocate(512 >> 2);
const iRootRule = allocate(3);
storeNode(iRootRule, rootRule);
treeData[RULES_PTR_SLOT] = iRootRule;
const iCharData = treeData.length << 2;
treeData[CHARDATA_PTR_SLOT] = iCharData;
const byteLength = (treeData.length << 2) + (charData.length + 3 & ~3);
allocateBuffers(byteLength);
pslBuffer32.set(treeData);
pslBuffer8.set(charData, treeData.length << 2);
}
fireChangedEvent();
};
/******************************************************************************/
const setHostnameArg = function(hostname) {
const buf = pslBuffer8;
if ( hostname === hostnameArg ) { return buf[LABEL_INDICES_SLOT]; }
if ( hostname === null || hostname.length === 0 ) {
hostnameArg = EMPTY_STRING;
return (buf[LABEL_INDICES_SLOT] = 0);
}
hostname = hostname.toLowerCase();
hostnameArg = hostname;
let n = hostname.length;
if ( n > 255 ) { n = 255; }
buf[LABEL_INDICES_SLOT] = n;
let i = n;
let j = LABEL_INDICES_SLOT + 1;
while ( i-- ) {
const c = hostname.charCodeAt(i);
if ( c === 0x2E /* '.' */ ) {
buf[j+0] = i + 1;
buf[j+1] = i;
j += 2;
}
buf[i] = c;
}
buf[j] = 0;
return n;
};
/******************************************************************************/
// Returns an offset to the start of the public suffix.
//
// WASM-able, because no information outside the buffer content is required.
const getPublicSuffixPosJS = function() {
const buf8 = pslBuffer8;
const buf32 = pslBuffer32;
const iCharData = buf32[CHARDATA_PTR_SLOT];
let iNode = pslBuffer32[RULES_PTR_SLOT];
let cursorPos = -1;
let iLabel = LABEL_INDICES_SLOT;
// Label-lookup loop
for (;;) {
// Extract label indices
const labelBeg = buf8[iLabel+1];
const labelLen = buf8[iLabel+0] - labelBeg;
// Match-lookup loop: binary search
let r = buf32[iNode+0] >>> 16;
if ( r === 0 ) { break; }
const iCandidates = buf32[iNode+2];
let l = 0;
let iFound = 0;
while ( l < r ) {
const iCandidate = l + r >>> 1;
const iCandidateNode = iCandidates + iCandidate + (iCandidate << 1);
const candidateLen = buf32[iCandidateNode+0] & 0x000000FF;
let d = labelLen - candidateLen;
if ( d === 0 ) {
const iCandidateChar = candidateLen <= 4
? iCandidateNode + 1 << 2
: iCharData + buf32[iCandidateNode+1];
for ( let i = 0; i < labelLen; i++ ) {
d = buf8[labelBeg+i] - buf8[iCandidateChar+i];
if ( d !== 0 ) { break; }
}
}
if ( d < 0 ) {
r = iCandidate;
} else if ( d > 0 ) {
l = iCandidate + 1;
} else /* if ( d === 0 ) */ {
iFound = iCandidateNode;
break;
}
}
// 2. If no rules match, the prevailing rule is "*".
if ( iFound === 0 ) {
if ( buf32[iCandidates + 1] !== 0x2A /* '*' */ ) { break; }
buf8[SUFFIX_NOT_FOUND_SLOT] = 1;
iFound = iCandidates;
}
iNode = iFound;
// 5. If the prevailing rule is a exception rule, modify it by
// removing the leftmost label.
if ( (buf32[iNode+0] & 0x00000200) !== 0 ) {
if ( iLabel > LABEL_INDICES_SLOT ) {
return iLabel - 2;
}
break;
}
if ( (buf32[iNode+0] & 0x00000100) !== 0 ) {
cursorPos = iLabel;
}
if ( labelBeg === 0 ) { break; }
iLabel += 2;
}
return cursorPos;
};
let getPublicSuffixPosWASM;
let getPublicSuffixPos = getPublicSuffixPosJS;
/******************************************************************************/
const getPublicSuffix = function(hostname) {
if ( pslBuffer32 === undefined ) { return EMPTY_STRING; }
const hostnameLen = setHostnameArg(hostname);
const buf8 = pslBuffer8;
if ( hostnameLen === 0 || buf8[0] === 0x2E /* '.' */ ) {
return EMPTY_STRING;
}
const cursorPos = getPublicSuffixPos();
if ( cursorPos === -1 ) {
return EMPTY_STRING;
}
const beg = buf8[cursorPos + 1];
return beg === 0 ? hostnameArg : hostnameArg.slice(beg);
};
/******************************************************************************/
const getDomain = function(hostname) {
if ( pslBuffer32 === undefined ) { return EMPTY_STRING; }
const hostnameLen = setHostnameArg(hostname);
const buf8 = pslBuffer8;
if ( hostnameLen === 0 || buf8[0] === 0x2E /* '.' */ ) {
return EMPTY_STRING;
}
const cursorPos = getPublicSuffixPos();
if ( cursorPos === -1 || buf8[cursorPos + 1] === 0 ) {
return EMPTY_STRING;
}
// 7. The registered or registrable domain is the public suffix plus one
// additional label.
const beg = buf8[cursorPos + 3];
return beg === 0 ? hostnameArg : hostnameArg.slice(beg);
};
/******************************************************************************/
const suffixInPSL = function(hostname) {
if ( pslBuffer32 === undefined ) { return false; }
const hostnameLen = setHostnameArg(hostname);
const buf8 = pslBuffer8;
if ( hostnameLen === 0 || buf8[0] === 0x2E /* '.' */ ) {
return false;
}
buf8[SUFFIX_NOT_FOUND_SLOT] = 0;
const cursorPos = getPublicSuffixPos();
return cursorPos !== -1 &&
buf8[cursorPos + 1] === 0 &&
buf8[SUFFIX_NOT_FOUND_SLOT] !== 1;
};
/******************************************************************************/
const toSelfie = function(encoder) {
if ( pslBuffer8 === undefined ) { return ''; }
if ( encoder instanceof Object ) {
const bufferStr = encoder.encode(pslBuffer8.buffer, pslByteLength);
return `${SELFIE_MAGIC}\t${bufferStr}`;
}
return {
magic: SELFIE_MAGIC,
buf32: pslBuffer32.subarray(0, pslByteLength >> 2),
};
};
const fromSelfie = function(selfie, decoder) {
let byteLength = 0;
if (
typeof selfie === 'string' &&
selfie.length !== 0 &&
decoder instanceof Object
) {
const pos = selfie.indexOf('\t');
if ( pos === -1 || selfie.slice(0, pos) !== `${SELFIE_MAGIC}` ) {
return false;
}
const bufferStr = selfie.slice(pos + 1);
byteLength = decoder.decodeSize(bufferStr);
if ( byteLength === 0 ) { return false; }
allocateBuffers(byteLength);
decoder.decode(bufferStr, pslBuffer8.buffer);
} else if (
selfie instanceof Object &&
selfie.magic === SELFIE_MAGIC &&
selfie.buf32 instanceof Uint32Array
) {
byteLength = selfie.buf32.length << 2;
allocateBuffers(byteLength);
pslBuffer32.set(selfie.buf32);
} else {
return false;
}
// Important!
hostnameArg = EMPTY_STRING;
pslBuffer8[LABEL_INDICES_SLOT] = 0;
fireChangedEvent();
return true;
};
/******************************************************************************/
// The WASM module is entirely optional, the JS implementation will be
// used should the WASM module be unavailable for whatever reason.
const enableWASM = (( ) => {
let wasmPromise;
const getWasmInstance = async function(wasmModuleFetcher, path) {
if ( typeof WebAssembly !== 'object' ) { return false; }
// The wasm code 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 false; }
try {
const module = await wasmModuleFetcher(`${path}publicsuffixlist`);
if ( module instanceof WebAssembly.Module === false ) {
return false;
}
const pageCount = pslBuffer8 !== undefined
? pslBuffer8.byteLength + 0xFFFF >>> 16
: 1;
const memory = new WebAssembly.Memory({ initial: pageCount });
const instance = await WebAssembly.instantiate(module, {
imports: { memory }
});
if ( instance instanceof WebAssembly.Instance === false ) {
return false;
}
const curPageCount = memory.buffer.byteLength >>> 16;
const newPageCount = pslBuffer8 !== undefined
? pslBuffer8.byteLength + 0xFFFF >>> 16
: 0;
if ( newPageCount > curPageCount ) {
memory.grow(newPageCount - curPageCount);
}
if ( pslBuffer32 !== undefined ) {
const buf8 = new Uint8Array(memory.buffer);
const buf32 = new Uint32Array(memory.buffer);
buf32.set(pslBuffer32);
pslBuffer8 = buf8;
pslBuffer32 = buf32;
}
wasmMemory = memory;
getPublicSuffixPosWASM = instance.exports.getPublicSuffixPos;
getPublicSuffixPos = getPublicSuffixPosWASM;
return true;
} catch(reason) {
console.info(reason);
}
return false;
};
return async function(wasmModuleFetcher, path) {
if ( getPublicSuffixPosWASM instanceof Function ) { return true; }
if ( wasmPromise instanceof Promise === false ) {
wasmPromise = getWasmInstance(wasmModuleFetcher, path);
}
return wasmPromise;
};
})();
const disableWASM = function() {
if ( getPublicSuffixPosWASM instanceof Function ) {
getPublicSuffixPos = getPublicSuffixPosJS;
getPublicSuffixPosWASM = undefined;
}
if ( wasmMemory === undefined ) { return; }
if ( pslBuffer32 !== undefined ) {
const buf8 = new Uint8Array(pslByteLength);
const buf32 = new Uint32Array(buf8.buffer);
buf32.set(pslBuffer32);
pslBuffer8 = buf8;
pslBuffer32 = buf32;
}
wasmMemory = undefined;
};
/******************************************************************************/
return ({
version: '2.0',
parse,
getDomain,
suffixInPSL,
getPublicSuffix,
toSelfie, fromSelfie,
disableWASM, enableWASM,
});
/******************************************************************************/
// <<<<<<<< end of anonymous namespace
})();

View File

@@ -0,0 +1,29 @@
### For code reviewers
All `wasm` files in that directory where created by compiling the
corresponding `wat` file using the command (using
`publicsuffixlist.wat`/`publicsuffixlist.wasm` as example):
wat2wasm publicsuffixlist.wat -o publicsuffixlist.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.
### See also
For the curious, the following online tool allows you to find out the machine
code as a result from the WASM code: https://mbebenita.github.io/WasmExplorer/

View File

@@ -0,0 +1,322 @@
;;
;; uBlock Origin - a browser extension to block requests.
;; Copyright (C) 2019-present Raymond Hill
;;
;; License: pick the one which suits you:
;; GPL v3 see <https://www.gnu.org/licenses/gpl.html>
;; APL v2 see <http://www.apache.org/licenses/LICENSE-2.0>
;;
;; Home: https://github.com/gorhill/publicsuffixlist.js
;; File: publicsuffixlist.wat
;;
;; Description: WebAssembly implementation for core lookup method in
;; publicsuffixlist.js
;;
;; How to compile:
;;
;; wat2wasm publicsuffixlist.wat -o publicsuffixlist.wasm
;;
;; The `wat2wasm` tool can be downloaded from an official WebAssembly
;; project:
;; https://github.com/WebAssembly/wabt/releases
(module
;;
;; module start
;;
(memory (import "imports" "memory") 1)
;;
;; Tree encoding in array buffer:
;;
;; Node:
;; + u8: length of char data
;; + u8: flags => bit 0: is_publicsuffix, bit 1: is_exception
;; + u16: length of array of children
;; + u32: char data or offset to char data
;; + u32: offset to array of children
;; = 12 bytes
;;
;; // i32 / i8
;; const HOSTNAME_SLOT = 0; // jshint ignore:line
;; const LABEL_INDICES_SLOT = 256; // -- / 256
;; const RULES_PTR_SLOT = 100; // 100 / 400
;; const SUFFIX_NOT_FOUND_SLOT = 399; // -- / 399
;; const CHARDATA_PTR_SLOT = 101; // 101 / 404
;; const EMPTY_STRING = '';
;; const SELFIE_MAGIC = 2;
;;
;;
;; Public functions
;;
;;
;; unsigned int getPublicSuffixPos()
;;
;; Returns an offset to the start of the public suffix.
;;
(func (export "getPublicSuffixPos")
(result i32) ;; result = match index, -1 = miss
(local $iCharData i32) ;; offset to start of character data
(local $iNode i32) ;; offset to current node
(local $iLabel i32) ;; offset to label indices
(local $cursorPos i32) ;; position of cursor within hostname argument
(local $labelBeg i32)
(local $labelLen i32)
(local $nCandidates i32)
(local $iCandidates i32)
(local $iFound i32)
(local $l i32)
(local $r i32)
(local $d i32)
(local $iCandidate i32)
(local $iCandidateNode i32)
(local $candidateLen i32)
(local $iCandidateChar i32)
(local $_1 i32)
(local $_2 i32)
(local $_3 i32)
;;
;; const iCharData = buf32[CHARDATA_PTR_SLOT];
i32.const 404
i32.load
local.set $iCharData
;; let iNode = pslBuffer32[RULES_PTR_SLOT];
i32.const 400
i32.load
i32.const 2
i32.shl
local.set $iNode
;; let iLabel = LABEL_INDICES_SLOT;
i32.const 256
local.set $iLabel
;; let cursorPos = -1;
i32.const -1
local.set $cursorPos
;; label-lookup loop
;; for (;;) {
block $labelLookupDone loop $labelLookup
;; // Extract label indices
;; const labelBeg = buf8[iLabel+1];
;; const labelLen = buf8[iLabel+0] - labelBeg;
local.get $iLabel
i32.load8_u
local.get $iLabel
i32.load8_u offset=1
local.tee $labelBeg
i32.sub
local.set $labelLen
;; // Match-lookup loop: binary search
;; let r = buf32[iNode+0] >>> 16;
;; if ( r === 0 ) { break; }
local.get $iNode
i32.load16_u offset=2
local.tee $r
i32.eqz
br_if $labelLookupDone
;; const iCandidates = buf32[iNode+2];
local.get $iNode
i32.load offset=8
i32.const 2
i32.shl
local.set $iCandidates
;; let l = 0;
;; let iFound = 0;
i32.const 0
local.tee $l
local.set $iFound
;; while ( l < r ) {
block $binarySearchDone loop $binarySearch
local.get $l
local.get $r
i32.ge_u
br_if $binarySearchDone
;; const iCandidate = l + r >>> 1;
local.get $l
local.get $r
i32.add
i32.const 1
i32.shr_u
local.tee $iCandidate
;; const iCandidateNode = iCandidates + iCandidate + (iCandidate << 1);
i32.const 2
i32.shl
local.tee $_1
local.get $_1
i32.const 1
i32.shl
i32.add
local.get $iCandidates
i32.add
local.tee $iCandidateNode
;; const candidateLen = buf32[iCandidateNode+0] & 0x000000FF;
i32.load8_u
local.set $candidateLen
;; let d = labelLen - candidateLen;
local.get $labelLen
local.get $candidateLen
i32.sub
local.tee $d
;; if ( d === 0 ) {
i32.eqz
if
;; const iCandidateChar = candidateLen <= 4
local.get $candidateLen
i32.const 4
i32.le_u
if
;; ? iCandidateNode + 1 << 2
local.get $iCandidateNode
i32.const 4
i32.add
local.set $iCandidateChar
else
;; : buf32[CHARDATA_PTR_SLOT] + buf32[iCandidateNode+1];
local.get $iCharData
local.get $iCandidateNode
i32.load offset=4
i32.add
local.set $iCandidateChar
end
;; for ( let i = 0; i < labelLen; i++ ) {
local.get $labelBeg
local.tee $_1
local.get $labelLen
i32.add
local.set $_3
local.get $iCandidateChar
local.set $_2
block $findDiffDone loop $findDiff
;; d = buf8[labelBeg+i] - buf8[iCandidateChar+i];
;; if ( d !== 0 ) { break; }
local.get $_1
i32.load8_u
local.get $_2
i32.load8_u
i32.sub
local.tee $d
br_if $findDiffDone
local.get $_1
i32.const 1
i32.add
local.tee $_1
local.get $_3
i32.eq
br_if $findDiffDone
local.get $_2
i32.const 1
i32.add
local.set $_2
br $findDiff
;; }
end end
;; }
end
;; if ( d < 0 ) {
;; r = iCandidate;
local.get $d
i32.const 0
i32.lt_s
if
local.get $iCandidate
local.set $r
br $binarySearch
end
;; } else if ( d > 0 ) {
;; l = iCandidate + 1;
local.get $d
i32.const 0
i32.gt_s
if
local.get $iCandidate
i32.const 1
i32.add
local.set $l
br $binarySearch
end
;; } else /* if ( d === 0 ) */ {
;; iFound = iCandidateNode;
;; break;
;; }
local.get $iCandidateNode
local.set $iFound
end end
;; }
;; // 2. If no rules match, the prevailing rule is "*".
;; if ( iFound === 0 ) {
;; if ( buf32[iCandidates + 1] !== 0x2A /* '*' */ ) { break; }
;; buf8[SUFFIX_NOT_FOUND_SLOT] = 1;
;; iFound = iCandidates;
;; }
local.get $iFound
i32.eqz
if
local.get $iCandidates
i32.load offset=4
i32.const 0x2A
i32.ne
br_if $labelLookupDone
i32.const 399
i32.const 1
i32.store8
local.get $iCandidates
local.set $iFound
end
;; iNode = iFound;
local.get $iFound
local.tee $iNode
;; // 5. If the prevailing rule is a exception rule, modify it by
;; // removing the leftmost label.
;; if ( (buf32[iNode+0] & 0x00000200) !== 0 ) {
;; if ( iLabel > LABEL_INDICES_SLOT ) {
;; return iLabel - 2;
;; }
;; break;
;; }
i32.load8_u offset=1
local.tee $_1
i32.const 0x02
i32.and
if
local.get $iLabel
i32.const 256
i32.gt_u
if
local.get $iLabel
i32.const -2
i32.add
return
end
br $labelLookupDone
end
;; if ( (buf32[iNode+0] & 0x00000100) !== 0 ) {
;; cursorPos = labelBeg;
;; }
local.get $_1
i32.const 0x01
i32.and
if
local.get $iLabel
local.set $cursorPos
end
;; if ( labelBeg === 0 ) { break; }
local.get $labelBeg
i32.eqz
br_if $labelLookupDone
;; iLabel += 2;
local.get $iLabel
i32.const 2
i32.add
local.set $iLabel
br $labelLookup
end end
local.get $cursorPos
)
;;
;; module end
;;
)