@@ -270,12 +270,8 @@ export const createFor = (
270270 const e2 = oldLength - endOffset
271271 const e3 = newLength - endOffset
272272
273- // parallel arrays holding blocks that need a mount or a move, in
274- // ascending index order (built packed, so a small change in a large
275- // list only allocates for the actual churn). After the reuse pass
276- // `queuedOldIndex[q]` distinguishes them: the block's old position
277- // for a reuse (its ForBlock is already in `newBlocks`), or -1 for a
278- // fresh mount.
273+ // new indices needing a mount or a move, ascending (built packed, so a
274+ // small change in a large list only allocates for the actual churn)
279275 const queuedIndices : number [ ] = [ ]
280276 const oldKeyIndexMap = new Map < any , number > ( )
281277
@@ -303,14 +299,19 @@ export const createFor = (
303299 }
304300
305301 let queuedLength = queuedIndices . length
306- const queuedOldIndex : number [ ] = new Array ( queuedLength )
302+ // `getSequence` input, doubling as the old-position record: the
303+ // block's old index + 1 (0 stays free as the "skip" marker, and also
304+ // marks a fresh mount). The bounds filter below zeroes reuses that may
305+ // not stay put; the apply pass tells move from mount by
306+ // `newBlocks[index]`, which the reuse pass has already filled in.
307+ const sources : number [ ] = new Array ( queuedLength )
307308 let mountCounter = 0
308309
309310 if ( oldKeyIndexMap . size === 0 ) {
310311 // pure append/replace: nothing to pair up. Plain loop over fill():
311312 // the inlined monomorphic store beats the generic builtin on a
312313 // freshly allocated (holey) array.
313- for ( let q = 0 ; q < queuedLength ; q ++ ) queuedOldIndex [ q ] = - 1
314+ for ( let q = 0 ; q < queuedLength ; q ++ ) sources [ q ] = 0
314315 mountCounter = queuedLength
315316 } else {
316317 for ( let q = queuedLength - 1 ; q >= 0 ; q -- ) {
@@ -321,9 +322,9 @@ export const createFor = (
321322 oldKeyIndexMap . delete ( key )
322323 const reusedBlock = ( newBlocks [ index ] = oldBlocks [ oldIndex ] )
323324 updateAt ( reusedBlock , source , index )
324- queuedOldIndex [ q ] = oldIndex
325+ sources [ q ] = oldIndex + 1
325326 } else {
326- queuedOldIndex [ q ] = - 1
327+ sources [ q ] = 0
327328 mountCounter ++
328329 }
329330 }
@@ -364,7 +365,7 @@ export const createFor = (
364365 // neighbours. Not minimal in general, but it never walks the
365366 // untouched majority.
366367 let sequence : number [ ] | undefined
367- let sequenceInput : number [ ] | undefined
368+ let allKept = false
368369 if ( mountCounter !== queuedLength ) {
369370 const span = queuedIndices [ queuedLength - 1 ] - queuedIndices [ 0 ] + 1
370371 if ( stationaryIndices && queuedLength * 2 >= span ) {
@@ -377,28 +378,28 @@ export const createFor = (
377378 if ( index > firstIndex && index < lastIndex ) {
378379 queuedIndices . push ( index )
379380 // a prefix stationary sits at the same index in both lists
380- queuedOldIndex . push ( index )
381+ sources . push ( index + 1 )
381382 }
382383 }
383384 if ( queuedIndices . length !== queuedLength ) {
384- sortQueueByIndex ( queuedIndices , queuedOldIndex )
385+ sortQueueByIndex ( queuedIndices , sources )
385386 queuedLength = queuedIndices . length
386387 }
387388 }
388389
389- // `getSequence` input: the old position (+1, so 0 stays free as its
390- // "skip" marker) of every block allowed to stay put, 0 for the rest.
391- sequenceInput = new Array ( queuedLength )
392390 let eligible = 0
391+ // if the eligible old positions already ascend, they are all in
392+ // relative order and every one of them stays put — no LIS needed
393+ let moved = false
394+ let maxSource = 0
393395 if ( queuedLength * 2 >= span ) {
394396 // dense: no bounds, every reuse competes for the LIS
395397 for ( let q = 0 ; q < queuedLength ; q ++ ) {
396- const oldIndex = queuedOldIndex [ q ]
397- if ( oldIndex < 0 ) {
398- sequenceInput [ q ] = 0
399- } else {
400- sequenceInput [ q ] = oldIndex + 1
398+ const value = sources [ q ]
399+ if ( value !== 0 ) {
401400 eligible ++
401+ if ( value < maxSource ) moved = true
402+ else maxSource = value
402403 }
403404 }
404405 } else {
@@ -423,23 +424,26 @@ export const createFor = (
423424 const nextIndex = queuedIndices [ segEnd ] + 1
424425 const upperBound = nextIndex < e3 ? nextIndex : e2
425426 for ( let q = seg ; q <= segEnd ; q ++ ) {
426- const oldIndex = queuedOldIndex [ q ]
427+ const value = sources [ q ]
428+ const oldIndex = value - 1
427429 if (
428430 oldIndex < 0 ||
429431 oldIndex <= lowerBound ||
430432 oldIndex >= upperBound
431433 ) {
432- sequenceInput [ q ] = 0
434+ sources [ q ] = 0
433435 } else {
434- sequenceInput [ q ] = oldIndex + 1
435436 eligible ++
437+ if ( value < maxSource ) moved = true
438+ else maxSource = value
436439 }
437440 }
438441 seg = segEnd + 1
439442 }
440443 }
441444
442- if ( eligible ) sequence = getSequence ( sequenceInput )
445+ if ( eligible && moved ) sequence = getSequence ( sources )
446+ else if ( eligible ) allKept = true
443447 }
444448
445449 // apply back-to-front so every block can anchor on the finalized
@@ -453,12 +457,12 @@ export const createFor = (
453457 let cachedAnchor : Node | undefined
454458 for ( let q = queuedLength - 1 ; q >= 0 ; q -- ) {
455459 const index = queuedIndices [ q ]
456- let isKept = false
460+ let isKept = allKept && sources [ q ] !== 0
457461 if ( sequenceEnd >= 0 && sequence ! [ sequenceEnd ] === q ) {
458462 sequenceEnd --
459463 // `getSequence` seeds its result with index 0, so it can report
460464 // a leading entry that was never selected; skip that marker
461- isKept = sequenceInput ! [ q ] !== 0
465+ isKept = sources [ q ] !== 0
462466 }
463467
464468 // A block that renders nothing has no first node, so look past it
@@ -501,7 +505,7 @@ export const createFor = (
501505 cachedAnchor = anchorNode
502506
503507 if ( isKept ) continue
504- if ( queuedOldIndex [ q ] >= 0 ) {
508+ if ( newBlocks [ index ] !== undefined ) {
505509 insertForBlock ( newBlocks [ index ] , anchorNode )
506510 } else {
507511 mount ( source , index , anchorNode )
0 commit comments