java-topology/defects/minecraft/patch/minecraft-0002-piston-resolver-hashset.md
russell@unturf.com a629bd0bbf no-stone-unturned wave: 8 new defects, 15 CLEAN confirmations; count 621→629
New defects (all PASS):
- exim-0001: same_hosts() MX-segment O(H²) → AVL set O(H log H), 10.5x at H=20
- minecraft-0001: DependencySorter.isCyclic no visited set O(E^D) → O(E), 342,000x at D=24
- minecraft-0002: PistonStructureResolver toPush ArrayList O(N²) → HashSet O(N)
- minecraft-0003: RedstoneWireEvaluator Deque.contains O(N²) → HashSet O(N)
- minecraft-0004: MoveThroughVillageGoal visited List O(N²) → HashSet O(N)
- mpich-0001: group_lpid_to_rank O(N²) → HashMap O(N), 313x at N=1000
- ompi-0001: group_overlap process-name scan O(N×M) → HashMap O(N+M), 2048x
- pcl-0001: RegionGrowing::getSegmentFromPoint O(C×S) → point_labels[] O(1), 50000x

CLEAN confirmed: esbuild, express, koa, ktor, lucene, mpich-recvq, ompi-startup,
  prosody, roda, rust/rustc-wave2, signal-server, solana, wiredtiger, wireguard-tools,
  linux-kernel (pointer to linux/)
2026-03-29 16:11:50 -04:00

1.4 KiB

Classification

Field Value
CWE CWE-407 Inefficient Algorithmic Complexity
Severity MEDIUM
Component net/minecraft/world/level/block/piston/PistonStructureResolver.java
Function resolve() BFS loop
Hot path Called every piston activation event

Defect

PistonStructureResolver.toPush is an ArrayList<BlockPos>. During piston BFS, toPush.contains(pos) is called for every candidate block — O(N) scan per call, inside the BFS loop: O(N²) total.

// BEFORE — O(N²): ArrayList.contains() inside BFS loop
private final List<BlockPos> toPush = Lists.newArrayList();
// ...
if (!this.toPush.contains(pos)) {
    this.toPush.add(pos);
}

At the 12-block push limit toPush is bounded, but the pattern propagates to modded environments with higher push limits.

Fix

Add a parallel HashSet<BlockPos> for O(1) duplicate detection.

// AFTER — O(N): HashSet.add() returns false on duplicate → O(1)
private final List<BlockPos> toPush = Lists.newArrayList();
private final Set<BlockPos> toPushSet = new HashSet<>();

// Replace: if (!this.toPush.contains(pos)) { this.toPush.add(pos); }
// With:    if (this.toPushSet.add(pos)) { this.toPush.add(pos); }

The List is retained for ordered iteration (piston push order matters). The Set is used only for membership testing.