Files
scriptc/tests/corpus/7142-array-read-filled-prefix.ts
T
Chris Tate 1917d1aa00 Test an array read's hole-free prefix before its capacity
The dense path of an element read that may be undefined (the
`%arr.idxOr` reads behind `a[i]` as `T | undefined`, the store
`a[x] = a[y]!`, and the element reads of the lowered array methods)
tested the index against the capacity, then against the hole-free prefix
(`filled`), and only above the prefix against the length and the slot's
state. `filled` is at most the length and the capacity (scr_array.h), so
an index below it needs no other test: the read now tests `filled` first
and takes the value path on that test alone. Above the prefix it tests
the capacity, the length and the state as before.

A double index first branches to the runtime lookup when it is not an
exact integer, then takes the same tests on its i64 value, which a 32-bit
target compares before truncating it.
2026-10-22 05:25:02 -05:00

73 lines
2.2 KiB
TypeScript

// Element reads that may be undefined (`a[i]` as `T | undefined`, and the
// stores `a[x] = a[y]!` read through) on arrays whose hole-free prefix
// changes: holes from writes past the end and from a longer length, present
// undefined, shrinking and regrowing, shift and unshift, out-of-range and
// negative indices, and reads at a loop counter and at a computed index.
function show(a: (number | undefined)[]): string {
const out: string[] = [];
for (let i = -1; i <= a.length; i++) {
const v = a[i];
out.push(v === undefined ? (i in a ? "u" : "_") : String(v));
}
return out.join(",");
}
const a: (number | undefined)[] = [10, 11, 12, 13, 14, 15];
console.log(show(a));
a[8] = 18;
console.log(show(a), 7 in a);
a.length = 9;
console.log(show(a));
a[7] = 17;
console.log(show(a));
a.length = 3;
console.log(show(a));
a.push(30, 31);
console.log(show(a));
a.unshift(1);
console.log(show(a));
a.shift();
a.shift();
console.log(show(a));
const withUndefined: (number | undefined)[] = [1, undefined, 3];
console.log(show(withUndefined));
// Copies through the read: a hole stores undefined, which `in` then sees.
function rotate(p: number[], r: number): void {
const first = p[0]!;
for (let i = 0; i < r; i++) p[i] = p[i + 1]!;
p[r] = first;
}
const p: number[] = [0, 1, 2, 3, 4];
rotate(p, 4);
console.log(p.join(","));
const holes: number[] = new Array<number>(4);
holes[0] = 0;
holes[1] = 1;
holes[3] = 3;
rotate(holes, 3);
console.log(holes.length, 1 in holes, holes[1] === undefined, holes.join(","));
// Reads at a computed index and at an index beyond the capacity.
function pick(src: number[], idx: number[]): string {
const out: string[] = [];
for (const k of idx) {
const v = src[k * 2];
out.push(v === undefined ? "-" : String(v));
}
return out.join(",");
}
const big: number[] = [];
for (let k = 0; k < 40; k++) big.push(k * k);
console.log(pick(big, [0, 1, 5, 19, 20, 1000, -1, 2 ** 31]));
big.length = 10;
console.log(pick(big, [0, 4, 5, 6]));
// Swaps at two counters, then reads of the swapped array.
const q: number[] = [5, 4, 3, 2, 1, 0];
for (let lo = 0, hi = q.length - 1; lo < hi; lo++, hi--) {
const t = q[lo]!;
q[lo] = q[hi]!;
q[hi] = t;
}
console.log(q.join(","), show(q));