Files
scriptc/tests/corpus/5751-string-length-cache.ts
T
Chris Tate ba8849a269 Speed up native string character tests, optional-string switches, fromCharCode and long-string length
`s.charAt(i) === "c"`, and `s[i] === "c"` where the index is proven in
bounds, built a one-character string to compare with the literal. When
the literal is one UTF-16 code unit, the comparison now reads
`s.charCodeAt(i)` and compares the number. charCodeAt converts the index
the same way charAt does, and an index out of range answers NaN, which
equals no unit, as "" equals no one-unit literal. A switch on
`s.charAt(i)` whose case tests are all one-unit literals switches on the
code unit the same way.

A switch on a `string | undefined` value with string literal cases, such
as an array element or an optional field, lowered to a chain of union
equalities. It now tests for undefined, which matches no case, and
dispatches the string through the string switch: by byte length, then by
the literal's leading bytes.

`String.fromCharCode(a, b, ...)` with up to sixteen arguments passes the
codes to the runtime in a stack array instead of a heap f64[], and up to
64 code units encode into a stack buffer instead of a heap scratch block.

`.length` of a string that is not ASCII counts its UTF-16 units. The
string index keeps four entries, so a program that read the lengths of
more long texts than that in turn (the text of every source file) walked
each whole string on every read. Lengths of non-ASCII strings longer than
64 bytes now go into a 1024-slot table keyed by address, allocated by
the first length it records. A slot is dropped when the string is freed
or reallocated and when an append changes it in place, which is where
the index drops its facts.
2026-10-20 01:11:36 -05:00

37 lines
1.4 KiB
TypeScript

// .length of long non-ASCII strings: many texts read in turn, appends in
// place (including a lone high surrogate completed by a low one), strings
// freed and rebuilt at reused addresses, split pieces, and slices and
// searches on texts whose lengths were read before.
let unit = "";
while (unit.length < 300) unit += "naïve 日本語 ";
const texts: string[] = [];
for (let i = 0; i < 40; i++) texts.push(unit.repeat(1 + (i % 7)) + "é".repeat(i));
let sum = 0;
for (let r = 0; r < 3; r++) for (const t of texts) sum += t.length;
console.log(sum, texts[39].length, texts[0].length);
let grow = unit;
const lengths: number[] = [];
for (let i = 0; i < 20; i++) {
grow += i % 2 ? "ü" : "x";
lengths.push(grow.length);
}
console.log(lengths.join(","));
let pair = unit + "\uD83D";
const before = pair.length;
pair += "\uDE00";
console.log(before, pair.length, pair.codePointAt(pair.length - 2)!.toString(16));
const seen: number[] = [];
for (let i = 0; i < 50; i++) {
const temp = unit + "ß".repeat(i % 5) + i;
seen.push(temp.length);
}
console.log(seen.slice(0, 10).join(","), seen.reduce((a, b) => a + b, 0));
const parts = (unit + "|").repeat(30).split("|");
console.log(parts.length, parts[0].length, parts.map((p) => p.length).reduce((a, b) => a + b, 0));
const big = texts[39];
console.log(big.slice(big.length - 5), big.substring(10, 20).length, big.indexOf("日本"), big.lastIndexOf("é"), big.length);