@@ -123,6 +123,7 @@ export const vnode_diff = (
123123 /// and is not connected to the tree.
124124 let vNewNode : VNode | null = null ;
125125
126+ let vSiblings : Map < string , VNode > | null = null ;
126127 /// The array even indices will contains keys and odd indices the non keyed siblings.
127128 let vSiblingsArray : Array < string | VNode | null > | null = null ;
128129
@@ -319,6 +320,7 @@ export const vnode_diff = (
319320 if ( descendVNode ) {
320321 assertDefined ( vCurrent || vNewNode , 'Expecting vCurrent to be defined.' ) ;
321322 vSideBuffer = null ;
323+ vSiblings = null ;
322324 vSiblingsArray = null ;
323325 vParent = ( vNewNode || vCurrent ! ) as ElementVNode | VirtualVNode ;
324326 vCurrent = vnode_getFirstChild ( vParent ) ;
@@ -331,6 +333,7 @@ export const vnode_diff = (
331333 const descendVNode = stack . pop ( ) ; // boolean: descendVNode
332334 if ( descendVNode ) {
333335 vSideBuffer = stack . pop ( ) ;
336+ vSiblings = stack . pop ( ) ;
334337 vSiblingsArray = stack . pop ( ) ;
335338 vNewNode = stack . pop ( ) ;
336339 vCurrent = stack . pop ( ) ;
@@ -346,7 +349,7 @@ export const vnode_diff = (
346349 function stackPush ( children : JSXChildren , descendVNode : boolean ) {
347350 stack . push ( jsxChildren , jsxIdx , jsxCount , jsxValue ) ;
348351 if ( descendVNode ) {
349- stack . push ( vParent , vCurrent , vNewNode , vSiblingsArray , vSideBuffer ) ;
352+ stack . push ( vParent , vCurrent , vNewNode , vSiblingsArray , vSiblings , vSideBuffer ) ;
350353 }
351354 stack . push ( descendVNode ) ;
352355 if ( Array . isArray ( children ) ) {
@@ -926,94 +929,85 @@ export const vnode_diff = (
926929 }
927930 }
928931
929- /**
930- * This function is used to retrieve the child with the given key. If the child is not found, it
931- * will return null.
932- *
933- * We will also collect all the keyed siblings found before the target key and add them to the
934- * side buffer. This is done to optimize the search for the next child with the specified key.
935- *
936- * @param nodeName - The name of the node.
937- * @param key - The key of the node.
938- * @returns The child with the given key or null if not found.
939- */
940932 function retrieveChildWithKey (
941933 nodeName : string | null ,
942934 key : string | null
943935 ) : ElementVNode | VirtualVNode | null {
944936 let vNodeWithKey : ElementVNode | VirtualVNode | null = null ;
945-
946- // if key is null we need to:
947- // - if this is the first time fill the vSiblingsArray with all siblings
948- // - if not then find the node we are interested in
949-
950- if ( key == null && vSiblingsArray != null ) {
951- for ( let i = 0 ; i < vSiblingsArray . length ; i += 2 ) {
952- if ( vSiblingsArray [ i ] === nodeName ) {
953- vNodeWithKey = vSiblingsArray ! [ i + 1 ] as ElementVNode | VirtualVNode ;
954- vSiblingsArray . splice ( i , 2 ) ;
955- break ;
937+ if ( vSiblings === null ) {
938+ // it is not materialized; so materialize it.
939+ vSiblings = new Map < string , VNode > ( ) ;
940+ vSiblingsArray = [ ] ;
941+ let vNode = vCurrent ;
942+ while ( vNode ) {
943+ const name = vnode_isElementVNode ( vNode ) ? vnode_getElementName ( vNode ) : null ;
944+ const vKey = getKey ( vNode ) || getComponentHash ( vNode , container . $getObjectById$ ) ;
945+ if ( vNodeWithKey === null && vKey == key && name == nodeName ) {
946+ vNodeWithKey = vNode as ElementVNode | VirtualVNode ;
947+ } else {
948+ if ( vKey === null ) {
949+ vSiblingsArray . push ( name , vNode ) ;
950+ } else {
951+ // we only add the elements which we did not find yet.
952+ vSiblings . set ( getSideBufferKey ( name , vKey ) , vNode ) ;
953+ }
954+ }
955+ vNode = vNode . nextSibling as VNode | null ;
956+ }
957+ } else {
958+ if ( key === null ) {
959+ for ( let i = 0 ; i < vSiblingsArray ! . length ; i += 2 ) {
960+ if ( vSiblingsArray ! [ i ] === nodeName ) {
961+ vNodeWithKey = vSiblingsArray ! [ i + 1 ] as ElementVNode | VirtualVNode ;
962+ vSiblingsArray ! . splice ( i , 2 ) ;
963+ break ;
964+ }
965+ }
966+ } else {
967+ const siblingsKey = getSideBufferKey ( nodeName , key ) ;
968+ if ( vSiblings . has ( siblingsKey ) ) {
969+ vNodeWithKey = vSiblings . get ( siblingsKey ) as ElementVNode | VirtualVNode ;
970+ vSiblings . delete ( siblingsKey ) ;
956971 }
957972 }
958- return vNodeWithKey ;
959973 }
960974
961- const fillSiblingsArray = vSiblingsArray == null ;
962- let vNode = vCurrent ;
963- let foundTarget = false ;
964- let keyedSiblingsBeforeTarget : Array < {
965- sideBufferKey : string ;
966- vNode : VNode ;
967- } > | null = null ;
975+ collectSideBufferSiblings ( vNodeWithKey ) ;
968976
969- while ( vNode ) {
970- const name = vnode_isElementVNode ( vNode ) ? vnode_getElementName ( vNode ) : null ;
971- const vKey = getKey ( vNode ) || getComponentHash ( vNode , container . $getObjectById$ ) ;
977+ return vNodeWithKey ;
978+ }
972979
973- if ( vNodeWithKey === null && vKey == key && name == nodeName ) {
974- vNodeWithKey = vNode as ElementVNode | VirtualVNode ;
975- foundTarget = true ;
976- if ( keyedSiblingsBeforeTarget && keyedSiblingsBeforeTarget . length > 0 ) {
980+ function collectSideBufferSiblings ( targetNode : VNode | null ) : void {
981+ if ( ! targetNode ) {
982+ if ( vCurrent ) {
983+ const name = vnode_isElementVNode ( vCurrent ) ? vnode_getElementName ( vCurrent ) : null ;
984+ const vKey = getKey ( vCurrent ) || getComponentHash ( vCurrent , container . $getObjectById$ ) ;
985+ if ( vKey != null ) {
986+ const sideBufferKey = getSideBufferKey ( name , vKey ) ;
977987 vSideBuffer ||= new Map ( ) ;
978- // Add all collected keyed siblings to side buffer now that we found the target
979- for ( const sibling of keyedSiblingsBeforeTarget ) {
980- vSideBuffer . set ( sibling . sideBufferKey , sibling . vNode ) ;
981- }
982- }
983- if ( ! fillSiblingsArray ) {
984- break ;
985- }
986- } else {
987- if ( vKey == null ) {
988- if ( fillSiblingsArray ) {
989- // Unkeyed sibling - add to siblings array
990- vSiblingsArray ||= [ ] ;
991- vSiblingsArray . push ( name , vNode ) ;
992- }
993- } else {
994- if ( ! foundTarget ) {
995- keyedSiblingsBeforeTarget ||= [ ] ;
996- const sideBufferKey = getSideBufferKey ( name , vKey ) ;
997- // Collect keyed sibling found before target
998- keyedSiblingsBeforeTarget . push ( { sideBufferKey, vNode } ) ;
999- }
988+ vSideBuffer . set ( sideBufferKey , vCurrent ) ;
989+ vSiblings ?. delete ( sideBufferKey ) ;
1000990 }
1001991 }
1002992
1003- vNode = vNode . nextSibling as VNode | null ;
993+ return ;
1004994 }
1005995
1006- // add current to the side buffer if it is not the target
1007- if ( ! foundTarget && vCurrent ) {
1008- const name = vnode_isElementVNode ( vCurrent ) ? vnode_getElementName ( vCurrent ) : null ;
1009- const vKey = getKey ( vCurrent ) || getComponentHash ( vCurrent , container . $getObjectById$ ) ;
996+ // Walk from vCurrent up to the target node and collect all keyed siblings
997+ let vNode = vCurrent ;
998+ while ( vNode && vNode !== targetNode ) {
999+ const name = vnode_isElementVNode ( vNode ) ? vnode_getElementName ( vNode ) : null ;
1000+ const vKey = getKey ( vNode ) || getComponentHash ( vNode , container . $getObjectById$ ) ;
1001+
10101002 if ( vKey != null ) {
10111003 const sideBufferKey = getSideBufferKey ( name , vKey ) ;
10121004 vSideBuffer ||= new Map ( ) ;
1013- vSideBuffer . set ( sideBufferKey , vCurrent ) ;
1005+ vSideBuffer . set ( sideBufferKey , vNode ) ;
1006+ vSiblings ?. delete ( sideBufferKey ) ;
10141007 }
1008+
1009+ vNode = vNode . nextSibling as VNode | null ;
10151010 }
1016- return vNodeWithKey ;
10171011 }
10181012
10191013 function getSideBufferKey ( nodeName : string | null , key : string ) : string ;
@@ -1055,6 +1049,7 @@ export const vnode_diff = (
10551049 ) : any {
10561050 // 1) Try to find the node among upcoming siblings
10571051 vNewNode = retrieveChildWithKey ( nodeName , lookupKey ) ;
1052+
10581053 if ( vNewNode ) {
10591054 vCurrent = vNewNode ;
10601055 vNewNode = null ;
0 commit comments