Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5417,6 +5417,7 @@ fn externalCoreFixtureModule(
stage_run.addFileInput(b.path("packages/core/src/control_content.ts"));
stage_run.addFileInput(b.path("packages/core/src/surface_recipes.ts"));
stage_run.addFileInput(b.path("packages/core/src/widget_metrics.ts"));
stage_run.addFileInput(b.path("packages/core/src/intrinsic_measure.ts"));
stage_run.addFileInput(b.path("packages/core/src/stream_policy.ts"));
stage_run.addArg("--src");
stage_run.addDirectoryArg(spec.src_dir);
Expand Down
1 change: 1 addition & 0 deletions build/app.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1258,6 +1258,7 @@ fn tsCoreStage(
stage_run.addFileInput(dep.path("packages/core/src/control_content.ts"));
stage_run.addFileInput(dep.path("packages/core/src/surface_recipes.ts"));
stage_run.addFileInput(dep.path("packages/core/src/widget_metrics.ts"));
stage_run.addFileInput(dep.path("packages/core/src/intrinsic_measure.ts"));
stage_run.addFileInput(dep.path("packages/core/src/stream_policy.ts"));
if (typescript_view) {
stage_run.addArg("--view-markup");
Expand Down
100 changes: 100 additions & 0 deletions packages/core/src/intrinsic_measure.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/** Per-node measurement continuations. Native supplies widget facts and executes
* the requested measurements; this policy selects recipes, order and stopping. */
function nscvIntrinsicMeasure(request: Uint8Array): Uint8Array {
if (request.length < 288 || request[0] !== 33 || request[1] !== 1 || request[2]! > 3 || request[3]! > 31)
throw new Error("invalid intrinsic measurement header");
const w = new DataView(request.buffer, request.byteOffset, request.byteLength), count = w.getUint32(12, true);
if (request.length !== 288 + count * 44 || w.getUint32(16, true) > 3 || w.getUint32(60, true) !== 0)
throw new Error("invalid intrinsic measurement shape");
const metric = request.slice(64, 288), m = new DataView(metric.buffer), kind = metric[7]!, flags = request[3]!, ready = w.getUint32(16, true);
if (metric[2] !== 32) throw new Error("invalid intrinsic measurement register operation");
const f = Math.fround, max = (a: number, b: number): number => nscvRenderExtreme(a, b, metric[3]!, true);
const v = (at: number): number => m.getFloat32(at, true), put = (at: number, value: number): void => { m.setFloat32(at, value, true); };
const scalar = (op: number, base: number = 0): number => {
metric[2] = op; put(16, base);
const r = nscvWidgetMetrics(metric); return new DataView(r.buffer).getFloat32(8, true);
};
// Validate the complete register packet even for a short-circuited container.
scalar(29);
const result = new Uint8Array(32), out = new DataView(result.buffer); out.setUint32(0, 1, true);
const command = (action: number, index: number = 0, value: number = 0): Uint8Array => {
out.setUint32(4, action, true); out.setUint32(8, index, true);
out.setFloat32(action === 1 ? 20 : 24, value, true); return result;
};
const done = (width: number, height: number): Uint8Array => { out.setFloat32(12, width, true); out.setFloat32(16, height, true); return result; };
const stopped = w.getUint32(4, true) >= w.getUint32(8, true);
const virtual = (flags & 1) !== 0, hasTitle = (m.getUint32(12, true) & 1) !== 0;
let op = 2, axis = 0;
if (kind === 1 || kind >= 8 && kind <= 13 || kind === 42 || kind === 44) op = 1;
else if (kind === 2 || kind === 4 || kind === 5 || kind === 7 || kind === 24 || kind === 25 || kind === 59 || kind === 60) { op = 1; axis = 1; }
else if (kind === 3) op = 4;
else if (kind === 6) op = 3;
else if (kind === 14) op = 7;
else if (kind === 18) op = 8;
else if (kind >= 19 && kind <= 21) op = 9;
else if (kind === 17) op = 10;
else if (!(kind === 0 || kind === 15 || kind === 16 || kind === 22 || kind === 23)) throw new Error("intrinsic measurement requires a container");
if ((kind === 3 || kind === 4 || kind === 5 || kind === 7) && virtual || kind === 6 && (virtual || (flags & 8) === 0)) return done(0, 0);
let textSize = 0;
if (op >= 8) textSize = kind === 17 ? scalar(1) : scalar(4, kind === 18 ? f(v(20) + 1) : v(40));
if (op >= 8 && (ready & 1) === 0) return command(1, 0, textSize);
if (kind === 42 && (ready & 2) === 0) return command(2);
const allowed = !stopped && !(kind === 14 && (flags & 4) === 0);
const statuses = 288 + count * 40;
for (let i = 0; i < count; i++) {
const childFlags = w.getUint32(288 + i * 40, true), status = w.getUint32(statuses + i * 4, true);
if (childFlags > 3 || status > 3 || status === 2 || (childFlags & 1) === 0 && status !== 0)
throw new Error("invalid intrinsic measurement child");
if (!allowed || (childFlags & 1) === 0) continue;
if (status === 0) return command(3, i);
if (op === 3 && status === 1) {
const child = new Uint8Array(136), cw = new DataView(child.buffer);
child[0] = 6; cw.setUint32(4, 1, true); child.set(request.subarray(288 + i * 40, 328 + i * 40), 96);
const bounded = nscvIntrinsicLayout(child, metric[3]!, metric[8]!, request[2]!); return command(4, i, new DataView(bounded.buffer).getFloat32(20, true));
}
}
const aggregate = new Uint8Array(96 + count * 40), a = new DataView(aggregate.buffer);
aggregate[0] = 6; aggregate[1] = op; aggregate[2] = axis; aggregate[3] = (stopped ? 1 : 0) | (hasTitle ? 2 : 0); a.setUint32(4, count, true);
aggregate.set(request.subarray(40, 56), 8);
const set = (at: number, value: number): void => { a.setFloat32(at, value, true); };
aggregate.set(metric.subarray(188, 196), 24);
for (let i = 0; i < 4; i++) aggregate.set(metric.subarray(152 + i * 4, 156 + i * 4), 32 + i * 4);
let gap = v(196);
if (kind === 9 || kind === 12) {
gap = max(0, gap);
if (!(gap > 0) && (kind === 9 ? (flags & 16) !== 0 : metric[6] === 1)) gap = max(0, w.getFloat32(kind === 9 ? 32 : 36, true));
}
set(48, gap);
if ((flags & 2) !== 0 && (kind === 17 || kind === 12 && metric[6] === 1)) {
const padding = scalar(kind === 17 ? 20 : 26); for (let i = 0; i < 4; i++) set(32 + i * 4, padding);
}
if (op >= 8) {
// The reference always measures text, including an empty title, but folds
// the single title line rather than the paragraph's maximum span scale.
aggregate.set(request.subarray(20, 24), 60); set(64, scalar(5, textSize));
const floorWidth = kind === 20 ? 360 : kind === 21 ? 320 : op === 9 ? 420 : 240;
const floorHeight = kind === 20 ? 280 : kind === 21 ? 420 : op === 9 ? 220 : op === 10 ? 52 : 120;
set(68, scalar(27, floorWidth)); set(72, scalar(27, floorHeight));
set(76, scalar(19, op === 9 ? w.getFloat32(56, true) : v(116)));
if (op === 10) { set(80, scalar(27, 16)); set(84, scalar(19, v(112))); set(88, scalar(30, v(120))); }
} else if (op === 7) set(64, max(scalar(10), f(scalar(5, scalar(1)) + f(scalar(19, v(116)) * 2))));
if (allowed) aggregate.set(request.subarray(288, statuses), 96);
const composed = nscvIntrinsicLayout(aggregate, metric[3]!, metric[8]!, request[2]!), c = new DataView(composed.buffer);
let width = c.getFloat32(4, true), height = c.getFloat32(8, true);
const signalingResult = (at: number): boolean => {
const word = c.getUint32(at, true);
return (metric[8]! & 16) !== 0 && (word & 0x7f800000) === 0x7f800000 && (word & 0x007fffff) !== 0 && (word & 0x00400000) === 0;
};
if (kind === 42) {
width = signalingResult(4) ? width : max(w.getFloat32(24, true), width); height = signalingResult(8) ? height : max(w.getFloat32(28, true), height);
if ((m.getUint32(12, true) & 128) !== 0 && Number.isFinite(v(124)) && v(124) > 0) width = f(Math.ceil(f(width * v(124))) / v(124));
}
done(width, height);
// Unmodified signaling floors stay raw through the copied ABI. A list row's
// optional snap performs arithmetic and therefore quiets its width instead.
if (op < 8) {
if (kind !== 42 || signalingResult(4) && ((m.getUint32(12, true) & 128) === 0 || !Number.isFinite(v(124)) || !(v(124) > 0))) result.set(composed.subarray(4, 8), 12);
if (kind !== 42 || signalingResult(8)) result.set(composed.subarray(8, 12), 16);
}
return result;
}
27 changes: 20 additions & 7 deletions packages/core/src/runtime_policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ export function native_window_policy(request: Uint8Array): Uint8Array {
if (request[0] === 30) return nscvControlContent(request);
if (request[0] === 31) return nscvSurfaceRecipes(request);
if (request[0] === 32) return nscvWidgetMetrics(request);
if (request[0] === 33) return nscvIntrinsicMeasure(request);
const data = new DataView(request.buffer, request.byteOffset, request.byteLength);
let at = 0;
const byte = (): number => {
Expand Down Expand Up @@ -1386,16 +1387,23 @@ function nscvContainerCross(child: NscContainerChild, axis: number, available: n
* portable policy. Native copies every result before recursive measurement;
* collection belongs to the enclosing app cycle, never to this operation.
*/
function nscvIntrinsicLayout(request: Uint8Array): Uint8Array {
function nscvIntrinsicLayout(request: Uint8Array, numericFlags: number = -1, signalingFlags: number = 0, maximumWords: number = 0): Uint8Array {
if (request.length < 96 || request[0] !== 6) throw new Error("invalid intrinsic layout request");
const op = request[1]!, axis = request[2]!, flags = request[3]!;
const wire = new DataView(request.buffer, request.byteOffset, request.byteLength), count = wire.getUint32(4, true);
if (op > 10 || axis > 1 || flags > 3 || wire.getUint32(92, true) !== 0 || request.length !== 96 + count * 40)
throw new Error("invalid intrinsic layout operation, flags or length");
const f = Math.fround, max = nscvSurfaceMax, min = nscvSurfaceMin;
const f = Math.fround;
const max = (a: number, b: number): number => numericFlags < 0 ? nscvSurfaceMax(a, b) : nscvRenderExtreme(a, b, numericFlags, true);
const min = (a: number, b: number): number => numericFlags < 0 ? nscvSurfaceMin(a, b) : nscvRenderExtreme(a, b, numericFlags, false);
const v = (at: number): number => wire.getFloat32(at, true);
const signalingAt = (at: number): boolean => {
const word = wire.getUint32(at, true);
return (word & 0x7f800000) === 0x7f800000 && (word & 0x007fffff) !== 0 && (word & 0x00400000) === 0;
};
const rightMax = (value: number, at: number): number => (signalingFlags & 16) !== 0 && signalingAt(at) ? v(at) : max(value, v(at));
const minWidth = v(24), minHeight = v(28), left = v(32), right = v(36), top = v(40), bottom = v(44);
const gap = max(0, v(48)), stopped = (flags & 1) !== 0, hasTitle = (flags & 2) !== 0;
const gap = rightMax(0, 48), stopped = (flags & 1) !== 0, hasTitle = (flags & 2) !== 0;
const result = new Uint8Array(12 + count * 12), out = new DataView(result.buffer); out.setUint32(0, count, true);
let flows = 0, width = 0, height = 0;
for (let i = 0; i < count; i++) {
Expand All @@ -1416,8 +1424,8 @@ function nscvIntrinsicLayout(request: Uint8Array): Uint8Array {
} else { width = max(width, w); height = max(height, op === 3 ? v(at + 36) : h); }
}
if (op === 0) return result;
const paddedWidth = (w: number): number => max(f(f(w + left) + right), minWidth);
const paddedHeight = (h: number): number => max(f(f(h + top) + bottom), minHeight);
const paddedWidth = (w: number): number => rightMax(f(f(w + left) + right), 24);
const paddedHeight = (h: number): number => rightMax(f(f(h + top) + bottom), 28);
if (op === 6) { width = paddedWidth(v(52)); height = paddedHeight(v(56)); }
else if (op === 7) {
width = paddedWidth(width); height = paddedHeight(f(f(v(64) + (height > 0 ? gap : 0)) + height));
Expand All @@ -1442,7 +1450,7 @@ function nscvIntrinsicLayout(request: Uint8Array): Uint8Array {
} else if (op === 5) {
if (stopped) { width = 0; height = 0; }
} else if (stopped || count === 0 || (op === 1 || op === 4) && flows === 0) {
width = op === 3 ? 0 : max(0, minWidth); height = max(0, minHeight);
width = op === 3 ? 0 : rightMax(0, 24); height = rightMax(0, 28);
} else {
if (op === 1) {
const gaps = f(gap * f(flows - 1));
Expand All @@ -1456,7 +1464,12 @@ function nscvIntrinsicLayout(request: Uint8Array): Uint8Array {
}
width = op === 3 ? 0 : paddedWidth(width); height = paddedHeight(height);
}
out.setFloat32(4, width, true); out.setFloat32(8, height, true); return result;
out.setFloat32(4, width, true); out.setFloat32(8, height, true);
if (op !== 5 && op < 8) {
if (op !== 3 && signalingAt(24) && ((maximumWords & 2) !== 0 || (maximumWords & 1) !== 0 && Number.isNaN(width))) result.set(request.subarray(24, 28), 4);
if (signalingAt(28) && ((maximumWords & 2) !== 0 || (maximumWords & 1) !== 0 && Number.isNaN(height))) result.set(request.subarray(28, 32), 8);
}
return result;
}

/** Width-aware sizing over stable widget-kind codes and native paragraph,
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/view_frontend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -926,4 +926,5 @@ const viewPrelude = "\n// Portable Native components compiled beside the committ
readFileSync(new URL("./control_content.ts", import.meta.url), "utf8") +
readFileSync(new URL("./surface_recipes.ts", import.meta.url), "utf8") +
readFileSync(new URL("./widget_metrics.ts", import.meta.url), "utf8") +
readFileSync(new URL("./intrinsic_measure.ts", import.meta.url), "utf8") +
readFileSync(new URL("./stream_policy.ts", import.meta.url), "utf8");
66 changes: 66 additions & 0 deletions packages/core/test/intrinsic_measure.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import assert from "node:assert/strict";
import test from "node:test";
import { readFileSync } from "node:fs";
import { stripTypeScriptTypes } from "node:module";
const source = ["runtime_policy.ts", "widget_metrics.ts", "intrinsic_measure.ts"].map(n => readFileSync(new URL(`../src/${n}`, import.meta.url), "utf8")).join("\n");
const { nscvIntrinsicMeasure: derive } = await import(`data:text/javascript;base64,${Buffer.from(stripTypeScriptTypes(source) + "\nexport {nscvIntrinsicMeasure};").toString("base64")}`);
function packet(kind: number, count = 0): Uint8Array {
const b = new Uint8Array(288 + count * 44), w = new DataView(b.buffer);
b.set([33, 1]); w.setUint32(8, 32, true); w.setUint32(12, count, true); b.set([32, 1, 32, 0, 0, 1, 0, kind], 64);
for (const [at, v] of [[20, 14], [24, 13], [28, 14], [40, 24], [44, 28], [48, 32], [52, 36], [56, 10], [60, 10], [64, 10], [88, 2], [108, 8], [112, 12], [116, 16], [120, 4], [124, 2]]) w.setFloat32(64 + at!, v!, true);
w.setFloat32(56, 24, true);
for (let i = 0; i < count; i++) w.setUint32(288 + i * 40, 1, true);
return b;
}
const word = (b: Uint8Array, at: number) => new DataView(b.buffer).getUint32(at, true);
const value = (b: Uint8Array, at: number) => new DataView(b.buffer).getFloat32(at, true);
test("horizontal scrolling requests each child's intrinsic and wrapped measurement before the next", () => {
const b = packet(6, 3), w = new DataView(b.buffer); b[3] = 8;
w.setUint32(328, 0, true); // Out-of-flow child is never admitted.
let out = derive(b); assert.equal(word(out, 4), 3); assert.equal(word(out, 8), 0);
w.setFloat32(292, 42.125, true); w.setFloat32(296, 14, true); w.setUint32(408, 1, true);
out = derive(b); assert.equal(word(out, 4), 4); assert.equal(word(out, 8), 0); assert.equal(value(out, 24), 42.125);
w.setFloat32(324, 35.25, true); w.setUint32(408, 3, true);
out = derive(b); assert.equal(word(out, 4), 3); assert.equal(word(out, 8), 2);
w.setFloat32(372, 18, true); w.setFloat32(376, 20, true); w.setFloat32(404, 70.5, true); w.setUint32(416, 3, true);
out = derive(b); assert.equal(word(out, 4), 0); assert.equal(value(out, 12), 0); assert.equal(value(out, 16), 70.5);
});
test("composed title measurement precedes children even for empty titles and capped traversal", () => {
const b = packet(19, 1), w = new DataView(b.buffer);
let out = derive(b); assert.equal(word(out, 4), 1); assert.equal(value(out, 20), 24);
w.setUint32(16, 1, true); out = derive(b); assert.equal(word(out, 4), 3);
w.setUint32(4, 32, true); out = derive(b); assert.equal(word(out, 4), 0); assert.equal(value(out, 12), 420); assert.equal(value(out, 16), 220);
});
test("depth virtualized scrolling and disclosure guards avoid child measurements", () => {
for (const kind of [0, 1, 2, 3, 4, 5, 7, 14]) {
const b = packet(kind, 2), w = new DataView(b.buffer); w.setUint32(4, 32, true);
assert.equal(word(derive(b), 4), 0);
}
for (const kind of [3, 4, 5, 6, 7]) { const b = packet(kind, 2); b[3] = 1; assert.equal(word(derive(b), 4), 0); }
const b = packet(14, 1); assert.equal(word(derive(b), 4), 0); b[3] = 4; assert.equal(word(derive(b), 4), 3);
});
test("results own complete zeroed records and never mutate borrowed requests", () => {
const b = packet(2, 1), before = b.slice(), out = derive(b), copied = out.slice();
assert.deepEqual(b, before); assert.deepEqual(out.subarray(12), new Uint8Array(20));
b.fill(255); derive(packet(19)); assert.deepEqual(out, copied);
});
test("the both-NaN maximum fact preserves a signaling floor without changing one-NaN selection", () => {
const b = packet(1, 1), w = new DataView(b.buffer);
w.setUint32(328, 1, true); w.setFloat32(292, 42.125, true); w.setFloat32(296, 14, true);
w.setUint32(252, 0x7f812345, true); w.setUint32(216, 0x7f812345, true);
assert.equal(word(derive(b), 12), 0x7fc12345);
b[2] = 1; assert.equal(word(derive(b), 12), 0x7f812345);
w.setFloat32(216, 0, true); assert.equal(value(derive(b), 12), 42.125);
// A target can choose a signaling argument and still quiet its word.
b[72] = 16; b[2] = 0; assert.equal(word(derive(b), 12), 0x7fc12345);
b[2] = 2; assert.equal(word(derive(b), 12), 0x7f812345);
});
test("malformed lengths enums status words and reserved bytes reject before measurement", () => {
for (const at of [0, 1, 2, 3, 16, 60, 64, 65, 67, 68, 71, 73, 264]) {
const b = packet(2); b[at] = 255; assert.throws(() => derive(b), /invalid/);
}
for (const status of [2, 4, 0xffffffff]) { const b = packet(2, 1); new DataView(b.buffer).setUint32(328, status, true); assert.throws(() => derive(b), /child/); }
assert.throws(() => derive(new Uint8Array(287)), /header/);
const b = packet(2, 1); new DataView(b.buffer).setUint32(12, 0xffffffff, true); assert.throws(() => derive(b), /shape/);
assert.throws(() => derive(packet(31)), /container/);
});
2 changes: 1 addition & 1 deletion packages/core/test/view_frontend.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ test("the view frontend and compiled portable component bundle typecheck", () =>
};
// Production joins these policies in one module before compiling the view.
const bundlePath = new URL("../src/view_components_typecheck.ts", import.meta.url).pathname;
const bundle = ["view_components.ts", "runtime_policy.ts", "control_appearance.ts", "component_construction.ts", "widget_motion.ts", "component_composition.ts", "code_content.ts", "chart_content.ts", "markdown_content.ts", "widget_audits.ts", "widget_routing.ts", "widget_changes.ts", "widget_paint.ts", "widget_presentation.ts", "widget_paint_walk.ts", "control_geometry.ts", "control_content.ts", "surface_recipes.ts", "widget_metrics.ts", "stream_policy.ts"]
const bundle = ["view_components.ts", "runtime_policy.ts", "control_appearance.ts", "component_construction.ts", "widget_motion.ts", "component_composition.ts", "code_content.ts", "chart_content.ts", "markdown_content.ts", "widget_audits.ts", "widget_routing.ts", "widget_changes.ts", "widget_paint.ts", "widget_presentation.ts", "widget_paint_walk.ts", "control_geometry.ts", "control_content.ts", "surface_recipes.ts", "widget_metrics.ts", "intrinsic_measure.ts", "stream_policy.ts"]
.map(name => readFileSync(new URL(`../src/${name}`, import.meta.url), "utf8")).join("\n");
const host = ts.createCompilerHost(options);
const readSource = host.getSourceFile.bind(host);
Expand Down
Loading
Loading