Skip to content

Commit a12cd91

Browse files
committed
clean extension code
Signed-off-by: Karim Taam <karim.t2am@gmail.com>
1 parent 459eb45 commit a12cd91

1 file changed

Lines changed: 56 additions & 93 deletions

File tree

ethereum/trie/src/main/java/org/hyperledger/besu/ethereum/trie/patricia/ParallelStoredMerklePatriciaTrie.java

Lines changed: 56 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)