Lesson 2
Link-buffer storage and `LinkComposer`
Compare the gap buffer with the experimental link-buffer path and verify its flag, default, and evidence.
Status: The two storage strategies are Version-specific . The link-buffer path is Experimental : this pinned source says it is disabled by default, and release notes still describe it as experimental.
Outcome
Explain what changes when LinkComposer replaces GapComposer, and state exactly when the current feature flag may be set.
“LinkTable” is a path, not a replacement
The Gerrit change is titled “Reimplement SlotTable as a link table.” The source package is composer.linkbuffer, and its table class is still SlotTable. Here, LinkTable is shorthand for this experimental path, not a public class or a claim that the gap buffer disappeared.
Both paths store groups and slots for reconciliation; their address mechanisms differ.
flowchart LR
Protocol[Composer protocol] --> Gap[GapComposer]
Protocol --> Link[LinkComposer]
Gap --> GapStore[Movable gaps]
Link --> LinkStore[Linked addresses]
GapStore --> Apply[Reconcile and apply]
LinkStore --> ApplyWhat the link buffer changes
The gap path stores a preorder tree in arrays and moves a gap for edits. The link-buffer path keeps backing arrays, but each group has linked next, parent, and child addresses. SlotTableAddressSpace also manages free group storage and slot ranges. The flag documents reducing array copies during deletion, movement, and reordering (SlotTableAddressSpace.kt, ComposeRuntimeFlags.kt).
The vocabulary changes too:
GroupAddressis an internal address, not an application ID.LinkAnchortracks a group address, becomes invalid when removed, and can follow movable content between address spaces (LinkAnchor.kt).GroupHandlepacks a target and context/predecessor for editor insert, move, or remove operations (GroupHandle.kt).SlotTableBuilder,SlotTableEditor, andSlotTableReaderbuild, edit, and traverse;LinkComposerconnects them to the common protocol (LinkComposer.kt).
These coordinates are not application-level identity.
Verify the current selection rule
At this pin, CompositionImpl uses isLinkBufferComposerEnabled: true selects linkbuffer.SlotTable and LinkComposer; false selects the gap-buffer table and GapComposer (Composition.kt). The flag defaults to false, must be set before first setContent, and must not change afterward.
The supplied R8 rule assumes false in minified release builds. At this pin, src/androidMain/keepRules/rules.keep contains:
-assumevalues public class androidx.compose.runtime.ComposeRuntimeFlags {
static boolean isLinkBufferComposerEnabled return false;
}
Current release notes likewise describe link-table as experimental and disabled by default.
Reproduce selection and selected workloads
This is a pinned procedure, not an upstream experiment executed by this production edit. In CompositionTest.kt, runTestWithComposer sets the flag to false for Gap and true for Link, before setContent, then restores it. Run paired composition and storage checks at revision cc1caf65677fc10a8ce8116eba46e716f4cef222:
./gradlew :compose:runtime:runtime:desktopTest \
--tests 'androidx.compose.runtime.CompositionTests.testRememberObserver_Abandon_Recompose_GapBuffer' \
--tests 'androidx.compose.runtime.CompositionTests.testRememberObserver_Abandon_Recompose_LinkTable' \
--tests 'androidx.compose.runtime.composer.gapbuffer.SlotTableTests.testInsertInTheMiddle' \
--tests 'androidx.compose.runtime.composer.gapbuffer.SlotTableTests.testRemoveInTheMiddle' \
--tests 'androidx.compose.runtime.composer.linkbuffer.SlotTableEditorTests.canReorder' \
--tests 'androidx.compose.runtime.composer.linkbuffer.SlotTableEditorTests.canRemoveMany'
For a timing harness, SlotTableIntegrationBenchmark.kt defines the exact 100-item removeAlternatingGroups and reverseGroups workloads. In a temporary uncommitted edit, set ComposeRuntimeFlags.isLinkBufferComposerEnabled to false for the gap control or true for the link run before the first setContent in ComposeBenchmarkBase.kt, and reset it afterward. Run each variant separately on a connected device:
./gradlew :compose:runtime:runtime:compose-runtime-benchmark:cC \
-Pandroid.testInstrumentationRunnerArguments.class=androidx.compose.runtime.benchmark.SlotTableIntegrationBenchmark#removeAlternatingGroups
./gradlew :compose:runtime:runtime:compose-runtime-benchmark:cC \
-Pandroid.testInstrumentationRunnerArguments.class=androidx.compose.runtime.benchmark.SlotTableIntegrationBenchmark#reverseGroups
The control and link runs should preserve the same composition result; compare timing only within each named workload. The default release R8 rule above forces false, so a minified cC link run also needs a temporary true -assumevalues override; otherwise stop at the non-minified correctness checks. These synthetic workloads and one device cannot establish universal performance or behavior for released artifacts.
Misconceptions
- “LinkTable replaced SlotTable everywhere.” It is an alternate, disabled-by-default path at this pin.
- “The flag can be flipped between recompositions.” The source requires a one-time choice before first content.
- “Linked storage is automatically faster.” Measure controlled workloads.
Check yourself
With the flag false, which composer and storage classes are selected? Which facts prevent calling it default?
Evidence notes
| Claim | Direct evidence | Status |
|---|---|---|
| The flag selects link-buffer or gap-buffer storage and composer | Composition.kt, ComposeRuntimeFlags.kt | Version-specific |
The library R8 rule assumes the shipped false default | Pinned src/androidMain/keepRules/rules.keep | Version-specific |
| Link-buffer uses linked groups, ranges, anchors, and handles | SlotTableAddresSpace.kt, LinkAnchor.kt, GroupHandle.kt | Version-specific
Experimental |
| Status is transitional; tests and benchmarks bound, but do not universalize, claims | Gerrit change 3695306, release notes, link-buffer tests, and SlotTableIntegrationBenchmark.kt | Experimental |
Freshness
Refresh when the flag default, release-note status, address-space classes, or composer selection changes.
Finished this lesson?
Your progress stays only in this browser.