`third_party/node/node_modules` is generated by `./mach vendor node`. Differential Revision: https://phabricator.services.mozilla.com/D320322
127 lines
3.6 KiB
JavaScript
127 lines
3.6 KiB
JavaScript
/*
|
||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||
Author Tobias Koppers @sokra
|
||
*/
|
||
|
||
"use strict";
|
||
|
||
// Character classification via a lookup table. A single bitmask test
|
||
// replaces the multi-comparison chains in each inner loop phase.
|
||
//
|
||
// BIT layout per character:
|
||
// bit 0 (STOP1 = 1): stops phase-1 scan (\n ; { })
|
||
// bit 1 (CONT2 = 2): continues phase-2 scan (; { } space \r \t)
|
||
//
|
||
// Phase 1: scan regular source chars that are NOT a phase-1 stop.
|
||
// Phase 2: consume runs of statement-boundary / whitespace chars.
|
||
// Phase 3: consume a trailing \n if present.
|
||
|
||
const STOP1 = 1;
|
||
const CONT2 = 2;
|
||
|
||
/** @type {Uint8Array} */
|
||
const CF = new Uint8Array(128);
|
||
CF[10] = STOP1; // \n – stops phase 1, NOT consumed in phase 2
|
||
CF[59] = STOP1 | CONT2; // ;
|
||
CF[123] = STOP1 | CONT2; // {
|
||
CF[125] = STOP1 | CONT2; // }
|
||
CF[32] = CONT2; // space
|
||
CF[13] = CONT2; // \r
|
||
CF[9] = CONT2; // \t
|
||
|
||
/**
|
||
* @callback OnPotentialToken
|
||
* @param {number} start start offset (inclusive)
|
||
* @param {number} end end offset (exclusive)
|
||
* @param {boolean} newline whether the token ends with a `\n`
|
||
* @returns {void}
|
||
*/
|
||
|
||
/**
|
||
* Streaming core: report each potential token by its `[start, end)` bounds
|
||
* instead of materialising substrings. The single real consumer
|
||
* (`OriginalSource.streamChunks`) slices on demand — and skips slicing
|
||
* entirely when emitting the final source (the `map()` / `sourceAndMap()`
|
||
* paths, which discard the chunk text) — so this avoids both the
|
||
* intermediate results array and every per-token `String.slice` allocation
|
||
* in the dominant case.
|
||
* @param {string} str string
|
||
* @param {OnPotentialToken} onToken called for each token
|
||
* @returns {void}
|
||
*/
|
||
const eachPotentialToken = (str, onToken) => {
|
||
const len = str.length;
|
||
let i = 0;
|
||
outer: while (i < len) {
|
||
const start = i;
|
||
// Phase 1 – skip regular (non-stop) characters
|
||
let cc = str.charCodeAt(i);
|
||
while (cc > 127 || !(CF[cc] & STOP1)) {
|
||
if (++i >= len) {
|
||
onToken(start, i, false);
|
||
break outer;
|
||
}
|
||
cc = str.charCodeAt(i);
|
||
}
|
||
// Phase 2 – consume delimiter / whitespace run (; { } space \r \t)
|
||
while (cc < 128 && CF[cc] & CONT2) {
|
||
if (++i >= len) {
|
||
onToken(start, i, false);
|
||
break outer;
|
||
}
|
||
cc = str.charCodeAt(i);
|
||
}
|
||
// Phase 3 – consume trailing newline
|
||
if (cc === 10) {
|
||
i++;
|
||
onToken(start, i, true);
|
||
} else {
|
||
onToken(start, i, false);
|
||
}
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Array-returning variant. Kept as a standalone loop rather than wrapping
|
||
* `eachPotentialToken` with a per-token callback: the callback indirection
|
||
* measurably slows this hot scan (V8 can no longer inline the slice/push),
|
||
* and the two only share the same small, well-tested classification table.
|
||
* @param {string} str string
|
||
* @returns {string[] | null} array of string separated by potential tokens
|
||
*/
|
||
const splitIntoPotentialTokens = (str) => {
|
||
const len = str.length;
|
||
if (len === 0) return null;
|
||
const results = [];
|
||
let i = 0;
|
||
outer: while (i < len) {
|
||
const start = i;
|
||
// Phase 1 – skip regular (non-stop) characters
|
||
let cc = str.charCodeAt(i);
|
||
while (cc > 127 || !(CF[cc] & STOP1)) {
|
||
if (++i >= len) {
|
||
results.push(str.slice(start, i));
|
||
break outer;
|
||
}
|
||
cc = str.charCodeAt(i);
|
||
}
|
||
// Phase 2 – consume delimiter / whitespace run (; { } space \r \t)
|
||
while (cc < 128 && CF[cc] & CONT2) {
|
||
if (++i >= len) {
|
||
results.push(str.slice(start, i));
|
||
break outer;
|
||
}
|
||
cc = str.charCodeAt(i);
|
||
}
|
||
// Phase 3 – consume trailing newline
|
||
if (cc === 10) {
|
||
i++;
|
||
}
|
||
results.push(str.slice(start, i));
|
||
}
|
||
return results;
|
||
};
|
||
|
||
module.exports = splitIntoPotentialTokens;
|
||
module.exports.eachPotentialToken = eachPotentialToken;
|