java-topology/defects/godot/patch/godot-0011-graph-edit-arranger-order-pred-linear-scan.patch

103 lines
5.1 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# UNDF: UNDF-2026-000000758
# UNDF: (leave blank)
--- a/scene/gui/graph_edit_arranger.cpp
+++ b/scene/gui/graph_edit_arranger.cpp
@@ -427,24 +427,33 @@ void GraphEditArranger::_calculate_inner_shifts(Dictionary &r_inner_shifts, con
float GraphEditArranger::_calculate_threshold(const StringName &p_v, const StringName &p_w, const Dictionary &r_node_names, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_inner_shift, real_t p_current_threshold, const HashMap<StringName, Vector2> &r_node_positions) {
#define MAX_ORDER 2147483647
-#define ORDER(node, layers) \
- for (unsigned int i = 0; i < layers.size(); i++) { \
- int index = layers[i].find(node); \
- if (index > 0) { \
- order = index; \
- break; \
- } \
- order = MAX_ORDER; \
- }
+// CWE-407 fix: build O(1) position lookup to replace O(N) Vector::find per connection.
+// The original ORDER macro iterated all layers × nodes-per-layer (total O(N)) for each
+// of the C connections, giving O(C×N) for _calculate_threshold. PRED had the same
+// pattern inside the do-while walk in _place_block (O(N) per walk step).
+// Fix: pass a pre-built HashMap<StringName,int> node_order that maps each node name
+// to its within-layer index. Look-up is O(1); total cost drops to O(N+C).
+#define ORDER(node, node_order_map) \
+ { \
+ const int *_op = node_order_map.getptr(node); \
+ order = _op ? *_op : MAX_ORDER; \
+ }
int order = MAX_ORDER;
float threshold = p_current_threshold;
+
+ // Build node_order: maps each node to its within-layer index.
+ // The original ORDER macro used `if (index > 0)` — position-0 nodes were treated
+ // as "not found" (returned MAX_ORDER). We preserve this: only store j >= 1.
+ // Nodes at j==0 will not be in the map, so getptr() returns nullptr → MAX_ORDER.
+ HashMap<StringName, int> node_order;
+ for (unsigned int i = 0; i < r_layers.size(); i++) {
+ for (int j = 1; j < r_layers[i].size(); j++) { // j=1: preserve original > 0 guard
+ node_order[r_layers[i][j]] = j;
+ }
+ }
+
if (p_v == p_w) {
int min_order = MAX_ORDER;
Ref<GraphEdit::Connection> incoming;
const Vector<Ref<GraphEdit::Connection>> connection_list = graph_edit->get_connections();
for (const Ref<GraphEdit::Connection> &connection : connection_list) {
if (connection->to_node == p_w) {
- ORDER(connection->from_node, r_layers);
+ ORDER(connection->from_node, node_order);
if (min_order > order) {
min_order = order;
incoming = connection;
@@ -468,7 +477,7 @@ float GraphEditArranger::_calculate_threshold(const StringName &p_v, const Stri
const Vector<Ref<GraphEdit::Connection>> connection_list = graph_edit->get_connections();
for (const Ref<GraphEdit::Connection> &connection : connection_list) {
if (connection->from_node == p_w) {
- ORDER(connection->to_node, r_layers);
+ ORDER(connection->to_node, node_order);
if (min_order > order) {
min_order = order;
outgoing = connection;
@@ -499,12 +508,20 @@ float GraphEditArranger::_calculate_threshold(const StringName &p_v, const Stri
void GraphEditArranger::_place_block(const StringName &p_v, float p_delta, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_node_name, const Dictionary &r_inner_shift, Dictionary &r_sink, Dictionary &r_shift, HashMap<StringName, Vector2> &r_node_positions) {
-#define PRED(node, layers) \
- for (unsigned int i = 0; i < layers.size(); i++) { \
- int index = layers[i].find(node); \
- if (index > 0) { \
- predecessor = layers[i][index - 1]; \
- break; \
- } \
- predecessor = StringName(); \
- }
+// CWE-407 fix: same O(N) Vector::find replaced with O(1) map look-up.
+// Build predecessor_map: node -> its predecessor (previous node in same layer).
+#define PRED(node, pred_map) \
+ { \
+ const StringName *_pp = pred_map.getptr(node); \
+ predecessor = _pp ? *_pp : StringName(); \
+ }
+
+ // Build predecessor_map once: maps each node (except first in its layer) to
+ // the node immediately before it in the same layer.
+ HashMap<StringName, StringName> predecessor_map;
+ for (unsigned int i = 0; i < r_layers.size(); i++) {
+ for (int j = 1; j < r_layers[i].size(); j++) {
+ predecessor_map[r_layers[i][j]] = r_layers[i][j - 1];
+ }
+ }
StringName predecessor;
StringName successor;
@@ -514,7 +531,7 @@ void GraphEditArranger::_place_block(const StringName &p_v, float p_delta, cons
if (pos.y == FLT_MAX) {
pos.y = 0;
bool initial = false;
StringName w = p_v;
real_t threshold = FLT_MIN;
do {
- PRED(w, r_layers);
+ PRED(w, predecessor_map);
if (predecessor != StringName()) {