From 7fa067c39beaa44d774f2c5365d8e1731bf5c26d Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Tue, 3 Nov 2026 06:54:06 -0800 Subject: [PATCH 1/2] chore(quest): plan keeping a pushed Vec's capacity as it shrinks Co-Authored-By: Claude Opus 5.5 --- quest/m3/README.md | 1 + quest/m3/ts-vec-clear-capacity.md | 33 +++++++++++++++++++++++++++++++ quest/m3/ts-vec-exact-capacity.md | 4 ++++ 3 files changed, 38 insertions(+) create mode 100644 quest/m3/ts-vec-clear-capacity.md diff --git a/quest/m3/README.md b/quest/m3/README.md index bbb2ba69b..93f723600 100644 --- a/quest/m3/README.md +++ b/quest/m3/README.md @@ -25,6 +25,7 @@ generic std gap is planned here even when moq never calls it. - [Debug of std's iterator adaptors](/quest/m3/ts-iter-adaptor-debug.md) - `Debug` of `Map`, `Zip`, `Peekable`, `DecodeUtf16`, and the other adaptors writes what std's does - [Waiting: a slice object for a mutable part of other values](/quest/m3/ts-slice-object.md) - waits on a moq profile showing the `Proxy` view's traps matter, then decides on an `rt.ByteSlice`-like form - [Port the workflow guard to kixelated/quest](/quest/m3/quest-workflow-guard.md) - quest's environment guard parses YAML as this repository's does +- [A vector keeps the capacity its pushes grew as it shrinks](/quest/m3/ts-vec-clear-capacity.md) - `clear`, `truncate`, `pop`, and `drain` keep a pushed `Vec`'s capacity, as std guarantees - [A vector's exact capacity as it shrinks](/quest/m3/ts-vec-exact-capacity.md) - a deque made from a `Vec` splits its slices where std's does - [A B-tree's searches call Ord as std's do](/quest/m3/ts-btree-search-order.md) - a B-tree's lookups and inserts call a key's own `Ord` in std's order and number - [A slice's chunks as mutable arrays](/quest/m3/ts-slice-conversions.md) - `as_chunks_mut` and `as_rchunks_mut` translate, writes landing in the slice diff --git a/quest/m3/ts-vec-clear-capacity.md b/quest/m3/ts-vec-clear-capacity.md new file mode 100644 index 000000000..dd38f1690 --- /dev/null +++ b/quest/m3/ts-vec-clear-capacity.md @@ -0,0 +1,33 @@ +# [S] A vector keeps the capacity its pushes grew as it shrinks + +## Goal + +[rust2-ts](/ts/README.md) keeps a `Vec`'s capacity through `clear`, `truncate`, `pop`, and +`drain` when the capacity came only from pushes, as std guarantees: `clear` and `truncate` +document that they leave the capacity unchanged, and a `Vec` never shrinks on its own. +Differential tests against native Rust prove it. + +## Plan + +Found in [moq-dev/rust2#522](https://github.com/moq-dev/rust2/pull/522), whose fix covers +only a `Vec` made from a deque's buffer. A `Vec` whose capacity is left implicit, grown by +pushes or made exact by `vecExact`, reads it back through `capOf` from its current length, +so the capacity falls with the length. Six pushes of 1280-byte elements, then `clear`: +native Rust reports a capacity of 8, TS reports 0. + +The guarantee-only model the maintainer chose on 2026-11-01 still has to keep this, since +it is one of std's guarantees, not the exact capacity +[that quest](/quest/m3/ts-vec-exact-capacity.md) leaves open. The `capacity` fixture +misses it because its `Known` checks capacity only against a floor that `with_capacity` or +`reserve` set, and those already hold their capacity. Its cases need a check that the +capacity never drops below what it was before a step that shortens the vector. + +Recommendation: record the implicit capacity once, when a step first drops the length +below it, so later steps grow from it. Measure on `push/stack` across engines first. A +record made on every pop cost 1.24x to 2.8x there in that quest's measurement. + +Public API: the translator's output. + +## Related + +- [A vector's exact capacity as it shrinks](/quest/m3/ts-vec-exact-capacity.md) - std's exact capacity, beyond its guarantees, so a deque made from a shrunk `Vec` splits its slices where std's does diff --git a/quest/m3/ts-vec-exact-capacity.md b/quest/m3/ts-vec-exact-capacity.md index 142a4f610..2ffb7654c 100644 --- a/quest/m3/ts-vec-exact-capacity.md +++ b/quest/m3/ts-vec-exact-capacity.md @@ -25,3 +25,7 @@ capacity the runtime models for the `Vec`, which holds only std's guarantees (de array, measured on the same workload before it is weighed against that decision. Public API: the translator's output. + +## Related + +- [A vector keeps the capacity its pushes grew as it shrinks](/quest/m3/ts-vec-clear-capacity.md) - the capacity std guarantees as the length drops, which the guarantee-only model must keep From 5693d2aa36cc1cf3f186c6aff014f5216436897d Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Tue, 3 Nov 2026 06:57:28 -0800 Subject: [PATCH 2/2] chore(quest): scope the pushed Vec capacity quest to what #527 leaves Co-Authored-By: Claude Opus 5.5 --- quest/m3/README.md | 2 +- quest/m3/ts-vec-clear-capacity.md | 49 +++++++++++++++++++++---------- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/quest/m3/README.md b/quest/m3/README.md index c2b61d270..4bdbd89b2 100644 --- a/quest/m3/README.md +++ b/quest/m3/README.md @@ -25,7 +25,7 @@ generic std gap is planned here even when moq never calls it. - [Debug of std's iterator adaptors](/quest/m3/ts-iter-adaptor-debug.md) - `Debug` of `Map`, `Zip`, `Peekable`, `DecodeUtf16`, and the other adaptors writes what std's does - [Waiting: a slice object for a mutable part of other values](/quest/m3/ts-slice-object.md) - waits on a moq profile showing the `Proxy` view's traps matter, then decides on an `rt.ByteSlice`-like form - [Port the workflow guard to kixelated/quest](/quest/m3/quest-workflow-guard.md) - quest's environment guard parses YAML as this repository's does -- [A vector keeps the capacity its pushes grew as it shrinks](/quest/m3/ts-vec-clear-capacity.md) - `clear`, `truncate`, `pop`, and `drain` keep a pushed `Vec`'s capacity, as std guarantees +- [A vector keeps the capacity its pushes grew as it shrinks](/quest/m3/ts-vec-clear-capacity.md) - every step that drops a pushed `Vec`'s length keeps its capacity, as std guarantees - [A vector's exact capacity as it shrinks](/quest/m3/ts-vec-exact-capacity.md) - a deque made from a `Vec` splits its slices where std's does - [A B-tree's searches call Ord as std's do](/quest/m3/ts-btree-search-order.md) - a B-tree's lookups and inserts call a key's own `Ord` in std's order and number - [A slice's chunks as mutable arrays](/quest/m3/ts-slice-conversions.md) - `as_chunks_mut` and `as_rchunks_mut` translate, writes landing in the slice diff --git a/quest/m3/ts-vec-clear-capacity.md b/quest/m3/ts-vec-clear-capacity.md index dd38f1690..a4d3278c7 100644 --- a/quest/m3/ts-vec-clear-capacity.md +++ b/quest/m3/ts-vec-clear-capacity.md @@ -2,25 +2,41 @@ ## Goal -[rust2-ts](/ts/README.md) keeps a `Vec`'s capacity through `clear`, `truncate`, `pop`, and -`drain` when the capacity came only from pushes, as std guarantees: `clear` and `truncate` -document that they leave the capacity unchanged, and a `Vec` never shrinks on its own. -Differential tests against native Rust prove it. +[rust2-ts](/ts/README.md) keeps a `Vec`'s capacity through every step that drops its +length, as std guarantees, when the capacity came only from pushes. That covers `clear`, +`truncate`, `pop`, `drain`, `retain`, `dedup`, `resize` down, `split_off`, `remove`, and +`swap_remove`. Differential tests against native Rust prove it. ## Plan -Found in [moq-dev/rust2#522](https://github.com/moq-dev/rust2/pull/522), whose fix covers -only a `Vec` made from a deque's buffer. A `Vec` whose capacity is left implicit, grown by -pushes or made exact by `vecExact`, reads it back through `capOf` from its current length, -so the capacity falls with the length. Six pushes of 1280-byte elements, then `clear`: -native Rust reports a capacity of 8, TS reports 0. - -The guarantee-only model the maintainer chose on 2026-11-01 still has to keep this, since -it is one of std's guarantees, not the exact capacity -[that quest](/quest/m3/ts-vec-exact-capacity.md) leaves open. The `capacity` fixture -misses it because its `Known` checks capacity only against a floor that `with_capacity` or -`reserve` set, and those already hold their capacity. Its cases need a check that the -capacity never drops below what it was before a step that shortens the vector. +std documents that `clear` and `truncate` leave the capacity unchanged, and that a `Vec` +never shrinks on its own. So the guarantee-only model the maintainer chose on 2026-11-01 +must keep this capacity. It is a guarantee, not the exact capacity +[that quest](/quest/m3/ts-vec-exact-capacity.md) leaves open. + +A `Vec` whose capacity is implicit, grown by pushes or made exact by `vecExact`, reads it +back through `capOf` from its current length, so the capacity falls with the length. +Two paths already keep it: [moq-dev/rust2#522](https://github.com/moq-dev/rust2/pull/522) +fixed a `Vec` made from a deque's buffer, and +[moq-dev/rust2#527](https://github.com/moq-dev/rust2/pull/527) fixed `clone_from`, which +records the capacity before its own truncate. `rt.vecTruncate` and the other steps still +lose it. + +Reproduction on #527's head: a `Vec<[u64; 160]>` gets six pushes, then one step, then a +read of `capacity()`. Native Rust reports 8 after every step. TS reports: + +| step | TS | +| --- | --- | +| `clear`, `pop` until empty, `drain(..)`, `retain` of none, `resize(0, _)` | 0 | +| `truncate(1)`, `dedup` to one | 1 | +| `split_off(2)` | 2 | +| `remove`, `swap_remove` | 8, only because five elements still imply 8 | +| `clone_from` of one element | 8 | + +The `capacity` fixture misses this because its `Known` checks capacity only against a +floor that `with_capacity` or `reserve` set, and those already hold their capacity. Its +cases need a check that the capacity never drops below what it was before a step that +shortens the vector. Recommendation: record the implicit capacity once, when a step first drops the length below it, so later steps grow from it. Measure on `push/stack` across engines first. A @@ -31,3 +47,4 @@ Public API: the translator's output. ## Related - [A vector's exact capacity as it shrinks](/quest/m3/ts-vec-exact-capacity.md) - std's exact capacity, beyond its guarantees, so a deque made from a shrunk `Vec` splits its slices where std's does +- `quest/m3/ts-clone-from-capacity.md`, added in [moq-dev/rust2#527](https://github.com/moq-dev/rust2/pull/527) - the capacity of a `Vec` that `clone_from` writes over inside a container; link it by path once #527 lands