@@ -338,8 +338,7 @@ private Node<V> handleBranchNode(
338338 }
339339
340340 /**
341- * Handles updates for an extension node. Attempts to parallelize by temporarily expanding the
342- * extension into branches, processing updates, then reconstructing if beneficial.
341+ * Handles updates for an extension node.
343342 *
344343 * @param extensionNode the extension node to update
345344 * @param location the location of the extension node
@@ -349,109 +348,97 @@ private Node<V> handleBranchNode(
349348 * @return the updated extension or restructured node
350349 */
351350 private Node <V > handleExtension (
352- final ExtensionNode <V > extensionNode ,
353- final Bytes location ,
354- final int depth ,
355- final List <UpdateEntry <V >> updates ,
356- final Optional <CommitCache > maybeCommitCache ) {
357-
358- final Bytes extensionPath = extensionNode .getPath ();
359- final int pathDepth = location .size ();
351+ final ExtensionNode <V > extensionNode ,
352+ final Bytes location ,
353+ final int depth ,
354+ final List <UpdateEntry <V >> updates ,
355+ final Optional <CommitCache > maybeCommitCache ) {
360356
361- // Check if all updates pass through this extension
362- if (allUpdatesMatchExtension (updates , pathDepth , extensionPath )) {
363- // All updates continue past extension: simply process child node
364- final Bytes newLocation = Bytes .concatenate (location , extensionPath );
365- final Node <V > childNode = extensionNode .getChild ();
366-
367- final Node <V > newChild =
368- processNode (
369- childNode , newLocation , depth + extensionPath .size (), updates , maybeCommitCache );
370-
371- // Create new extension with updated child
372- final Node <V > newExtension = extensionNode .replaceChild (newChild );
373- commitOrHashNode (newExtension , location , maybeCommitCache );
374- return newExtension ;
375- }
376-
377- // Updates diverge: check if parallelization is worth it
357+ // Multiple updates: expand to enable parallelization
378358 if (updates .size () > 1 ) {
379- // Expand extension into branch structure, process in parallel, then optimize
359+ final Bytes extensionPath = extensionNode .getPath ();
360+ final int pathDepth = location .size ();
361+ // Find where updates diverge
362+ int divergenceIndex = findDivergencePoint (updates , pathDepth , extensionPath );
380363 return expandExtensionToDivergencePoint (
381- extensionNode , extensionPath , location , depth , updates , maybeCommitCache );
364+ extensionNode , extensionPath , location , depth , updates , maybeCommitCache , divergenceIndex );
382365 }
383366
384- // Single update or not worth parallelizing : use sequential processing
367+ // Single update: use sequential visitor
385368 return applyUpdatesSequentially (extensionNode , location , updates , maybeCommitCache );
386369 }
387370
388371 /**
389- * Expands an extension node into a branch structure to enable parallel processing. After
390- * processing, the structure is automatically optimized (may recreate extensions).
391- *
392- * <p>Strategy: 1. Create branches for each nibble in the extension path 2. Place the extension's
393- * child at the end of this chain 3. Process all updates through this expanded structure 4. Let
394- * the trie's natural optimization (maybeFlatten) recreate extensions if needed
372+ * Expands extension into branches up to divergence point.
395373 *
396374 * @param extensionNode the extension to expand
397375 * @param extensionPath the path of the extension
398376 * @param location the current location
399377 * @param depth the current depth
400378 * @param updates the updates to apply
401379 * @param maybeCommitCache optional commit cache
402- * @return the processed and optimized node structure
380+ * @param divergenceIndex the index where updates diverge (already computed)
381+ * @return the processed node structure
403382 */
404383 private Node <V > expandExtensionToDivergencePoint (
405- final ExtensionNode <V > extensionNode ,
406- final Bytes extensionPath ,
407- final Bytes location ,
408- final int depth ,
409- final List <UpdateEntry <V >> updates ,
410- final Optional <CommitCache > maybeCommitCache ) {
411-
412- final int pathDepth = location .size ();
413-
414- // Find the first position within the extension where updates diverge
415- int divergenceIndex = findDivergencePoint (updates , pathDepth , extensionPath );
384+ final ExtensionNode <V > extensionNode ,
385+ final Bytes extensionPath ,
386+ final Bytes location ,
387+ final int depth ,
388+ final List <UpdateEntry <V >> updates ,
389+ final Optional <CommitCache > maybeCommitCache ,
390+ final int divergenceIndex ) {
391+
392+ // Handle no-divergence
393+ if (divergenceIndex == extensionPath .size ()) {
394+ // All updates continue past extension - just update child
395+ final Bytes newLocation = Bytes .concatenate (location , extensionPath );
396+ final Node <V > newChild = processNode (
397+ extensionNode .getChild (),
398+ newLocation ,
399+ depth + extensionPath .size (),
400+ updates ,
401+ maybeCommitCache );
416402
417- // Split extension at divergence point
418- // Example: Extension[3,5,7,9], divergence at index 2 (position 7)
419- // commonPrefix = [3,5]
420- // divergingNibble = 7 (from extension)
421- // remainingSuffix = [9]
403+ final Node <V > newExtension = extensionNode .replaceChild (newChild );
404+ commitOrHashNode (newExtension , location , maybeCommitCache );
405+ return newExtension ;
406+ }
422407
408+ // Handle divergence
409+ // Split: [0..divergenceIndex) = common, [divergenceIndex..end) = remaining
423410 final Bytes commonPrefix = extensionPath .slice (0 , divergenceIndex );
424- final byte divergingNibble = extensionPath .get (divergenceIndex );
425- final Bytes remainingSuffix = extensionPath .slice (divergenceIndex + 1 );
411+ final Bytes remainingExtension = extensionPath .slice (divergenceIndex );
426412
413+ // Load and build continuation
427414 Node <V > originalChild = loadNode (extensionNode .getChild ());
428-
429- // Create continuation: Extension[D,E] → originalChild (or just originalChild if empty)
430- Node <V > continuation =
431- remainingSuffix .isEmpty ()
415+ Node <V > currentNode = remainingExtension .isEmpty ()
432416 ? originalChild
433- : nodeFactory .createExtension (remainingSuffix , originalChild );
434-
435- // Create branch at divergence point with continuation as one child
436- final List <Node <V >> branchChildren =
437- new ArrayList <>(Collections .nCopies (16 , NullNode .instance ()));
438- branchChildren .set (divergingNibble , continuation );
439- Node <V > currentNode = nodeFactory .createBranch (branchChildren , Optional .empty ());
417+ : nodeFactory .createExtension (remainingExtension , originalChild );
440418
419+ // Build branch chain for common prefix
441420 for (int i = commonPrefix .size () - 1 ; i >= 0 ; i --) {
442421 final byte nibble = commonPrefix .get (i );
443422 final List <Node <V >> children = new ArrayList <>(Collections .nCopies (16 , NullNode .instance ()));
444423 children .set (nibble , currentNode );
445424 currentNode = nodeFactory .createBranch (children , Optional .empty ());
446425 }
447426
448- // Now process all updates through this structure
449- // The branch will naturally handle grouping and parallelization
450427 return processNode (currentNode , location , depth , updates , maybeCommitCache );
451428 }
452429
430+ /**
431+ * Finds the first position within the extension path where ANY update diverges.
432+ *
433+ * @param updates the updates to check
434+ * @param baseDepth the base depth (location.size())
435+ * @param extensionPath the extension path
436+ * @return the index in extensionPath where first divergence occurs, or extensionPath.size() if none
437+ */
453438 private int findDivergencePoint (
454- final List <UpdateEntry <V >> updates , final int baseDepth , final Bytes extensionPath ) {
439+ final List <UpdateEntry <V >> updates ,
440+ final int baseDepth ,
441+ final Bytes extensionPath ) {
455442
456443 for (int i = 0 ; i < extensionPath .size (); i ++) {
457444 final int absolutePosition = baseDepth + i ;
@@ -563,30 +550,6 @@ private BranchNode<V> buildEmptyBranch() {
563550 return (BranchNode <V >) nodeFactory .createBranch (children , Optional .empty ());
564551 }
565552
566- /**
567- * Checks if all updates in the list match the given extension path.
568- *
569- * @param updates the updates to check
570- * @param depth the current depth in the trie
571- * @param extensionPath the extension path to match against
572- * @return true if all updates match, false otherwise
573- */
574- private boolean allUpdatesMatchExtension (
575- final List <UpdateEntry <V >> updates , final int depth , final Bytes extensionPath ) {
576- final int extSize = extensionPath .size ();
577- for (UpdateEntry <V > entry : updates ) {
578- if (entry .path .size () < depth + extSize ) {
579- return false ;
580- }
581- for (int i = 0 ; i < extSize ; i ++) {
582- if (entry .path .get (depth + i ) != extensionPath .get (i )) {
583- return false ;
584- }
585- }
586- }
587- return true ;
588- }
589-
590553 /**
591554 * Groups updates by their nibble at the specified depth.
592555 *
0 commit comments