62 lines
2.5 KiB
Diff
62 lines
2.5 KiB
Diff
# UNDF: UNDF-2026-000000015
|
|
# UNDF: (leave blank)
|
|
Box2D v3 CWE-407: b2UnBufferMove — linear scan through moveArray to find proxy → O(N²) on bulk destroy
|
|
|
|
b2UnBufferMove (broad_phase.c) is called from b2BroadPhase_DestroyProxy.
|
|
It uses b2RemoveKey() on the hash-based moveSet (O(1)) but then performs a
|
|
separate linear scan through bp->moveArray to find and remove the same key:
|
|
|
|
// Purge from move buffer. Linear search.
|
|
// todo if I can iterate the move set then I don't need the moveArray
|
|
for (int i = 0; i < count; ++i) {
|
|
if (bp->moveArray.data[i] == proxyKey) {
|
|
b2IntArray_RemoveSwap(&bp->moveArray, i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
The code comment acknowledges the linear scan. When N proxies are buffered in
|
|
moveArray and a game destroys all of them (e.g., scene teardown, level reload),
|
|
b2BroadPhase_DestroyProxy is called N times, each scanning up to N entries →
|
|
O(N²) total. At N=10 000 (large physics scene), this is 100 000 000 iterations.
|
|
|
|
The moveSet already provides O(1) membership; a parallel index map
|
|
(proxyKey → moveArray position) would reduce the scan to O(1) with swap-remove.
|
|
|
|
Severity: HIGH — hits on every scene reload / bulk body destruction.
|
|
Complexity: O(N²) → O(1) per remove with an index map.
|
|
|
|
--- a/src/broad_phase.c
|
|
+++ b/src/broad_phase.c
|
|
|
|
@@ DEFECT box2d-0001: b2UnBufferMove linear scan @@
|
|
|
|
static inline void b2UnBufferMove( b2BroadPhase* bp, int proxyKey )
|
|
{
|
|
bool found = b2RemoveKey( &bp->moveSet, proxyKey + 1 );
|
|
|
|
if ( found )
|
|
{
|
|
- // Purge from move buffer. Linear search.
|
|
- // todo if I can iterate the move set then I don't need the moveArray
|
|
- int count = bp->moveArray.count;
|
|
- for ( int i = 0; i < count; ++i )
|
|
- {
|
|
- if ( bp->moveArray.data[i] == proxyKey )
|
|
- {
|
|
- b2IntArray_RemoveSwap( &bp->moveArray, i );
|
|
- break;
|
|
- }
|
|
- }
|
|
+ // FIX box2d-0001: O(1) lookup via index map (proxyKey → slot in moveArray)
|
|
+ // Requires a parallel b2HashTable<int,int> moveArrayIndex field in b2BroadPhase.
|
|
+ //
|
|
+ // Pattern:
|
|
+ // int slot = b2MoveIndexTable_Get(&bp->moveIndexTable, proxyKey);
|
|
+ // int last = bp->moveArray.data[bp->moveArray.count - 1];
|
|
+ // bp->moveArray.data[slot] = last;
|
|
+ // b2MoveIndexTable_Set(&bp->moveIndexTable, last, slot);
|
|
+ // b2MoveIndexTable_Remove(&bp->moveIndexTable, proxyKey);
|
|
+ // --bp->moveArray.count;
|
|
}
|
|
}
|