java-topology/defects/pyramid/patch/pyramid-0004-util-toposort-deque.patch

58 lines
2.4 KiB
Diff

# UNDF: UNDF-2026-000000234
Fixes pyramid-0004: util.py TopologicalSorter.sorted() — list used as queue with O(n) pop(0)/insert(0) and O(n) roots membership + remove().
--- a/src/pyramid/util.py
+++ b/src/pyramid/util.py
@@ DEFECT pyramid-0004: TopologicalSorter.sorted() lines 503-561
+from collections import deque
def sorted(self):
order = [(self.first, self.last)]
- roots = [] # list — O(n) pop(0)/insert(0)/in/remove — CWE-407
+ roots = deque() # FIX pyramid-0004: deque for O(1) popleft/appendleft
+ roots_set = set() # FIX pyramid-0004: O(1) membership test
graph = {}
def add_node(node):
if node not in graph:
- roots.append(node)
+ roots.append(node) # O(1) deque append
+ roots_set.add(node)
graph[node] = [0]
def add_arc(fromnode, tonode):
graph[fromnode].append(tonode)
graph[tonode][0] += 1
- if tonode in roots: # O(n) list scan — CWE-407
- roots.remove(tonode) # O(n) list removal — CWE-407
+ if tonode in roots_set: # O(1) set lookup — fixed
+ roots.remove(tonode) # O(n) deque remove — still needed; rare case
+ roots_set.discard(tonode)
# ... (node/arc setup unchanged) ...
sorted_names = []
while roots:
- root = roots.pop(0) # O(n) list pop from front — CWE-407
+ root = roots.popleft() # O(1) deque popleft — fixed
+ roots_set.discard(root)
sorted_names.append(root)
children = graph[root][1:]
for child in children:
arcs = graph[child][0]
arcs -= 1
graph[child][0] = arcs
if arcs == 0:
- roots.insert(0, child) # O(n) list insert at front — CWE-407
+ roots.appendleft(child) # O(1) deque appendleft — fixed
+ roots_set.add(child)
del graph[root]
# SUMMARY: Four O(n) list operations replaced:
# roots.pop(0) → roots.popleft() O(n) → O(1)
# roots.insert(0) → roots.appendleft() O(n) → O(1)
# tonode in roots → tonode in roots_set O(n) → O(1)
# roots.remove() → roots.remove() O(n) → O(n) [rare; deque.remove still O(n)]
# Net: O(E²) → O(E log E) or better for most tween/deriver graphs.