Files
scriptc/tests/corpus/array-primitive-ordering.ts
Chris Tate 4ec55cdf64 Accelerate default primitive array ordering (#721)
- Sort primitive arrays with stable native ordering and pure conversion keys.
- Preserve sparse semantics and argument effects while avoiding callback overhead.
- Verify Unicode ordering and ownership across native and WASI paths.
2026-10-07 08:15:08 -05:00

67 lines
3.3 KiB
TypeScript

// Default ordering compares String conversions and preserves tied values.
const values = [NaN, 2, 10, -0, 0, -0, -1, -10, Infinity, -Infinity, 1e21, 1e-7, 1e20, 1e-6];
const original = values.map(value => String(value)).join(",");
const copied = values.toSorted();
console.log(original === values.map(value => String(value)).join(","), copied !== values);
console.log(copied.map(value => value === 0 ? String(1 / value) : String(value)).join(","));
console.log(values.sort() === values, values.map(value => value === 0 ? String(1 / value) : String(value)).join(","));
console.log([true, false, true, false].sort().join(","));
// Prefixes, embedded NULs, and mismatches within and between UTF-8 units.
const alphabet = ["", "\0", "a\0", "a", "é", "ê", "中", "\ud7ff", "\ue000", "\uffff", "\u{10000}", "\u{10001}", "😀", "😁", "\u{10ffff}"];
for (const prefix of ["", "same-prefix-", "é😀same-prefix-"]) {
console.log(JSON.stringify(alphabet.map(word => prefix + word).toSorted()));
}
for (const size of [0, 1, 2, 15, 16, 17, 31, 65, 129, 2051]) {
const words: string[] = [];
for (let i = 0; i < size; i++) words.push(alphabet[(i * 71) % alphabet.length]!);
const sorted = words.toSorted();
console.log(size, JSON.stringify(sorted), JSON.stringify(words.sort()) === JSON.stringify(sorted));
console.log(JSON.stringify(sorted.toSorted()) === JSON.stringify(sorted));
console.log(JSON.stringify(sorted.toReversed().sort()) === JSON.stringify(sorted));
}
const holes: string[] = [];
holes[4] = "two";
holes[1] = "one";
holes.fill(undefined as unknown as string, 3, 4);
holes[-1] = "property";
const materialized = holes.toSorted();
console.log(materialized.length, materialized.join("|"), materialized.map((_, index) => index).join(","));
console.log(holes.sort() === holes, holes.join("|"), holes.map((_, index) => index).join(","), holes[-1]);
const sparse: number[] = [];
sparse[1048581] = -0;
sparse[10] = 0;
sparse[3] = 2;
sparse[1] = 10;
sparse.fill(undefined as unknown as number, 1048580, 1048581);
sparse[-1] = 77;
sparse.sort();
console.log(sparse.length, sparse.slice(0, 6).map((_, index) => index).join(","), sparse[-1]);
console.log(sparse.slice(0, 4).map(value => value === 0 ? 1 / value : value).join(","));
console.log(Object.hasOwn(sparse, 4), Object.hasOwn(sparse, 5), Object.hasOwn(sparse, 1048581));
// An explicit effectful comparator still uses its ordinary callback path.
let comparisons = 0;
const callback = [3, 1, 2].toSorted((a, b) => { comparisons++; return a - b; });
console.log(callback.join(","), comparisons > 0);
let receiver = [3, 2, 1];
const saved = receiver;
const order: string[] = [];
function getReceiver(): number[] { order.push("receiver"); return receiver; }
function defaultOrder(): number {
order.push("argument");
receiver[0] = 10;
receiver = [9, 8];
return 0;
}
console.log(getReceiver().toSorted(void defaultOrder()).join(","), saved.join(","), receiver.join(","), order.join(","));
console.log(receiver.sort(undefined).join(","));
// An erased assertion cannot replace a real comparator with default order.
console.log([2, 10, 1].toSorted(((a: number, b: number) => a - b) as unknown as undefined).join(","));
function fail(): number { throw new Error("argument failed"); }
const untouched = [2, 10, 1];
try { untouched.sort(void fail()); } catch (error) { console.log(error instanceof Error, untouched.join(",")); }