perf(core): avoid double allocation in packMotionPlans

Rewrite MotionPlans.packMotionPlans to precompute total word count and fill a
single pre-sized Uint32Array with an index pointer (no intermediate number[]).
This commit is contained in:
scamiv
2026-02-25 22:12:33 +01:00
parent 422072675e
commit efe3b1e9e5
+56 -41
View File
@@ -41,68 +41,83 @@ export type MotionPlanRecord = GridPathPlan | TrainRailPathPlan;
export function packMotionPlans(
records: readonly MotionPlanRecord[],
): Uint32Array {
const out: number[] = [MOTION_PLANS_SCHEMA_VERSION, records.length];
let totalWords = 2;
for (const record of records) {
switch (record.kind) {
case "grid": {
const path =
record.path instanceof Uint32Array
? record.path
: Uint32Array.from(record.path);
const pathLen = (record.path.length >>> 0) as number;
totalWords += 2 + 5 + pathLen;
break;
}
case "train": {
const carCount = (record.carUnitIds.length >>> 0) as number;
const pathLen = (record.path.length >>> 0) as number;
totalWords += 2 + 7 + carCount + pathLen;
break;
}
}
}
const out = new Uint32Array(totalWords);
out[0] = MOTION_PLANS_SCHEMA_VERSION >>> 0;
out[1] = records.length >>> 0;
let offset = 2;
for (const record of records) {
switch (record.kind) {
case "grid": {
const path = record.path as ArrayLike<number>;
const pathLen = path.length >>> 0;
const wordCount = 2 + 5 + pathLen;
out.push(
PackedMotionPlanKind.GridPathSet,
wordCount,
record.unitId >>> 0,
record.planId >>> 0,
record.startTick >>> 0,
record.ticksPerStep >>> 0,
pathLen,
);
for (let i = 0; i < path.length; i++) {
out.push(path[i] >>> 0);
out[offset++] = PackedMotionPlanKind.GridPathSet;
out[offset++] = wordCount >>> 0;
out[offset++] = record.unitId >>> 0;
out[offset++] = record.planId >>> 0;
out[offset++] = record.startTick >>> 0;
out[offset++] = record.ticksPerStep >>> 0;
out[offset++] = pathLen >>> 0;
for (let i = 0; i < pathLen; i++) {
out[offset++] = path[i] >>> 0;
}
break;
}
case "train": {
const carUnitIds =
record.carUnitIds instanceof Uint32Array
? record.carUnitIds
: Uint32Array.from(record.carUnitIds);
const carUnitIds = record.carUnitIds as ArrayLike<number>;
const carCount = carUnitIds.length >>> 0;
const path =
record.path instanceof Uint32Array
? record.path
: Uint32Array.from(record.path);
const path = record.path as ArrayLike<number>;
const pathLen = path.length >>> 0;
const wordCount = 2 + 7 + carCount + pathLen;
out.push(
PackedMotionPlanKind.TrainRailPathSet,
wordCount,
record.engineUnitId >>> 0,
record.planId >>> 0,
record.startTick >>> 0,
record.speed >>> 0,
record.spacing >>> 0,
carCount,
pathLen,
);
for (let i = 0; i < carUnitIds.length; i++) {
out.push(carUnitIds[i] >>> 0);
out[offset++] = PackedMotionPlanKind.TrainRailPathSet;
out[offset++] = wordCount >>> 0;
out[offset++] = record.engineUnitId >>> 0;
out[offset++] = record.planId >>> 0;
out[offset++] = record.startTick >>> 0;
out[offset++] = record.speed >>> 0;
out[offset++] = record.spacing >>> 0;
out[offset++] = carCount >>> 0;
out[offset++] = pathLen >>> 0;
for (let i = 0; i < carCount; i++) {
out[offset++] = carUnitIds[i] >>> 0;
}
for (let i = 0; i < path.length; i++) {
out.push(path[i] >>> 0);
for (let i = 0; i < pathLen; i++) {
out[offset++] = path[i] >>> 0;
}
break;
}
}
}
return new Uint32Array(out);
if (offset !== out.length) {
throw new Error(
`packMotionPlans size mismatch: wrote ${offset}, expected ${out.length}`,
);
}
return out;
}
export function unpackMotionPlans(packed: Uint32Array): {