264 lines
8.4 KiB
Diff
264 lines
8.4 KiB
Diff
# UNDF: UNDF-2026-000000999
|
|
--- a/src/map.c
|
|
+++ b/src/map.c
|
|
@@ -2840,6 +2840,8 @@
|
|
/**
|
|
* @brief Node structure for A* pathfinding.
|
|
*/
|
|
+/* CWE-407: A_in() and A_lowest() scan linked lists in O(N), making
|
|
+ the Dijkstra loop O(V^2 + E*V). Replace with array-indexed visited. */
|
|
typedef struct SysNode_ {
|
|
struct SysNode_ *next; /**< Next node */
|
|
struct SysNode_ *gnext; /**< Next node in the garbage collector. */
|
|
@@ -2853,45 +2855,57 @@
|
|
/* prototypes */
|
|
static SysNode *A_newNode( StarSystem *sys );
|
|
static int A_g( const SysNode *n );
|
|
-static double A_d( const SysNode *n );
|
|
-static int A_less( const SysNode *op1, const SysNode *op2 );
|
|
-static SysNode *A_add( SysNode *first, SysNode *cur );
|
|
-static SysNode *A_rm( SysNode *first, const StarSystem *cur );
|
|
-static SysNode *A_in( SysNode *first, const StarSystem *cur );
|
|
-static SysNode *A_lowest( SysNode *first );
|
|
static void A_freeList( SysNode *first );
|
|
static int map_decorator_parse( MapDecorator *temp, const char *file );
|
|
+
|
|
+/* Visited/cost tracking arrays indexed by system id. */
|
|
+static SysNode **A_open_idx = NULL; /**< open set: system id -> node or NULL */
|
|
+static SysNode **A_close_idx = NULL; /**< closed set: system id -> node or NULL */
|
|
+static int A_idx_sz = 0; /**< size of index arrays */
|
|
+
|
|
+static void A_idx_ensure( int n )
|
|
+{
|
|
+ if ( n <= A_idx_sz )
|
|
+ return;
|
|
+ A_open_idx = realloc( A_open_idx, sizeof( SysNode * ) * n );
|
|
+ A_close_idx = realloc( A_close_idx, sizeof( SysNode * ) * n );
|
|
+ A_idx_sz = n;
|
|
+}
|
|
+
|
|
+static void A_idx_clear( int n )
|
|
+{
|
|
+ A_idx_ensure( n );
|
|
+ memset( A_open_idx, 0, sizeof( SysNode * ) * n );
|
|
+ memset( A_close_idx, 0, sizeof( SysNode * ) * n );
|
|
+}
|
|
+
|
|
/** @brief Creates a new node link to star system. */
|
|
static SysNode *A_newNode( StarSystem *sys )
|
|
{
|
|
@@ -2909,65 +2923,48 @@
|
|
{
|
|
return n->g;
|
|
}
|
|
-/** @brief Gets the d from a node. */
|
|
-static double A_d( const SysNode *n )
|
|
-{
|
|
- return n->d;
|
|
-}
|
|
-/** @brief op1 is less than op2. */
|
|
-static int A_less( const SysNode *op1, const SysNode *op2 )
|
|
-{
|
|
- return ( A_g( op1 ) < A_g( op2 ) ) ||
|
|
- ( A_g( op1 ) == A_g( op2 ) && A_d( op1 ) < A_d( op2 ) );
|
|
-}
|
|
-/** @brief Adds a node to the linked list. */
|
|
-static SysNode *A_add( SysNode *first, SysNode *cur )
|
|
+/** @brief Inserts node into open list sorted by (g, d). O(N) insert but
|
|
+ * avoids O(N) extract-min and O(N) membership tests. */
|
|
+static SysNode *A_addSorted( SysNode *first, SysNode *cur )
|
|
{
|
|
- SysNode *n;
|
|
-
|
|
- if ( first == NULL )
|
|
+ A_open_idx[cur->sys->id] = cur;
|
|
+ if ( first == NULL ) {
|
|
+ cur->next = NULL;
|
|
return cur;
|
|
-
|
|
- n = first;
|
|
- while ( n->next != NULL )
|
|
- n = n->next;
|
|
- n->next = cur;
|
|
-
|
|
- return first;
|
|
-}
|
|
-/* @brief Removes a node from a linked list. */
|
|
-static SysNode *A_rm( SysNode *first, const StarSystem *cur )
|
|
-{
|
|
- SysNode *n, *p;
|
|
-
|
|
- if ( first->sys == cur ) {
|
|
- n = first->next;
|
|
- first->next = NULL;
|
|
- return n;
|
|
}
|
|
-
|
|
- p = first;
|
|
- n = p->next;
|
|
- do {
|
|
- if ( n->sys == cur ) {
|
|
- p->next = n->next;
|
|
- n->next = NULL;
|
|
- break;
|
|
- }
|
|
- p = n;
|
|
- } while ( ( n = n->next ) != NULL );
|
|
-
|
|
- return first;
|
|
+ /* Insert before first element that is worse. */
|
|
+ if ( cur->g < first->g ||
|
|
+ ( cur->g == first->g && cur->d < first->d ) ) {
|
|
+ cur->next = first;
|
|
+ return cur;
|
|
+ }
|
|
+ SysNode *p = first;
|
|
+ while ( p->next != NULL &&
|
|
+ ( p->next->g < cur->g ||
|
|
+ ( p->next->g == cur->g && p->next->d <= cur->d ) ) )
|
|
+ p = p->next;
|
|
+ cur->next = p->next;
|
|
+ p->next = cur;
|
|
+ return first;
|
|
}
|
|
-/** @brief Checks to see if node is in linked list. */
|
|
-static SysNode *A_in( SysNode *first, const StarSystem *cur )
|
|
+/** @brief Removes a node from open list. O(N) worst case but amortized
|
|
+ * with sorted-insert we no longer need a separate A_lowest scan. */
|
|
+static SysNode *A_rmOpen( SysNode *first, const StarSystem *cur )
|
|
{
|
|
- SysNode *n;
|
|
-
|
|
- if ( first == NULL )
|
|
- return NULL;
|
|
-
|
|
- n = first;
|
|
- do {
|
|
- if ( n->sys == cur )
|
|
- return n;
|
|
- } while ( ( n = n->next ) != NULL );
|
|
- return NULL;
|
|
-}
|
|
-/** @brief Returns the lowest ranking node from a linked list of nodes. */
|
|
-static SysNode *A_lowest( SysNode *first )
|
|
-{
|
|
- SysNode *lowest, *n;
|
|
-
|
|
- if ( first == NULL )
|
|
+ A_open_idx[cur->id] = NULL;
|
|
+ if ( first == NULL || first->sys == cur ) {
|
|
+ SysNode *n = first ? first->next : NULL;
|
|
+ if ( first )
|
|
+ first->next = NULL;
|
|
return NULL;
|
|
-
|
|
- n = first;
|
|
- lowest = n;
|
|
- do {
|
|
- if ( A_less( n, lowest ) )
|
|
- lowest = n;
|
|
- } while ( ( n = n->next ) != NULL );
|
|
- return lowest;
|
|
+ }
|
|
+ SysNode *p = first;
|
|
+ while ( p->next != NULL ) {
|
|
+ if ( p->next->sys == cur ) {
|
|
+ SysNode *rem = p->next;
|
|
+ p->next = rem->next;
|
|
+ rem->next = NULL;
|
|
+ return first;
|
|
+ }
|
|
+ p = p->next;
|
|
+ }
|
|
+ return first;
|
|
}
|
|
/** @brief Frees a linked list. */
|
|
static void A_freeList( SysNode *first )
|
|
@@ -3051,14 +3048,18 @@
|
|
const vec2 *p_pos_entry = ( ojumps > 0 ) ? NULL : posstart;
|
|
if ( ojumps > 0 ) {
|
|
|
|
+ /* Initialize index arrays for O(1) membership tests. */
|
|
+ int nsys = array_size( systems_stack );
|
|
+ A_idx_clear( nsys );
|
|
+
|
|
/* start the linked lists */
|
|
open = closed = NULL;
|
|
cur = A_newNode( ssys );
|
|
cur->parent = NULL;
|
|
cur->g = 0;
|
|
cur->d = 0.0;
|
|
cur->pos = p_pos_entry;
|
|
- open = A_add( open, cur ); /* Initial open node is the start system */
|
|
+ open = A_addSorted( open, cur ); /* Initial open node is the start system */
|
|
|
|
j = 0;
|
|
- while ( ( cur = A_lowest( open ) ) ) {
|
|
+ while ( open != NULL ) {
|
|
+ cur = open; /* Head of sorted list is always the lowest cost. */
|
|
int cost;
|
|
/* End condition. */
|
|
if ( cur->sys == esys )
|
|
@@ -3074,7 +3075,9 @@
|
|
|
|
/* Get best from open and toss to closed */
|
|
- open = A_rm( open, cur->sys );
|
|
- closed = A_add( closed, cur );
|
|
+ open = open->next;
|
|
+ A_open_idx[cur->sys->id] = NULL;
|
|
+ cur->next = closed;
|
|
+ closed = cur;
|
|
+ A_close_idx[cur->sys->id] = cur;
|
|
cost = A_g( cur ) + 1; /* Base unit is jump and always increases by 1. */
|
|
|
|
for ( int i = 0; i < array_size( cur->sys->jumps ); i++ ) {
|
|
@@ -3096,20 +3099,24 @@
|
|
if ( !show_hidden && jp_isFlag( jp, JP_HIDDEN ) )
|
|
continue;
|
|
|
|
/* Update cost */
|
|
- const SysNode n_cost = { .g = cost,
|
|
- .d = A_d( cur ) +
|
|
- ( ( cur->pos != NULL )
|
|
- ? vec2_dist( cur->pos, &jp->pos )
|
|
- : 0.0 ) };
|
|
+ int n_g = cost;
|
|
+ double n_d = cur->d + ( ( cur->pos != NULL )
|
|
+ ? vec2_dist( cur->pos, &jp->pos )
|
|
+ : 0.0 );
|
|
|
|
/* Check to see if it's already in the closed set. */
|
|
- ccost = A_in( closed, sys );
|
|
- if ( ( ccost != NULL ) && !A_less( &n_cost, ccost ) )
|
|
+ ccost = A_close_idx[sys->id]; /* O(1) lookup */
|
|
+ if ( ccost != NULL &&
|
|
+ !( n_g < ccost->g ||
|
|
+ ( n_g == ccost->g && n_d < ccost->d ) ) )
|
|
continue;
|
|
|
|
/* Remove if it exists and current is better. */
|
|
- ocost = A_in( open, sys );
|
|
+ ocost = A_open_idx[sys->id]; /* O(1) lookup */
|
|
if ( ocost != NULL ) {
|
|
- if ( A_less( &n_cost, ocost ) )
|
|
- open = A_rm( open, sys ); /* New path is better */
|
|
+ if ( n_g < ocost->g ||
|
|
+ ( n_g == ocost->g && n_d < ocost->d ) )
|
|
+ open = A_rmOpen( open, sys ); /* New path is better */
|
|
else
|
|
continue; /* This node is worse, so ignore it. */
|
|
}
|
|
@@ -3118,9 +3125,9 @@
|
|
const JumpPoint *jp_entry = jump_getTarget( cur->sys, sys );
|
|
neighbour = A_newNode( sys );
|
|
neighbour->parent = cur;
|
|
- neighbour->g = n_cost.g;
|
|
- neighbour->d = n_cost.d;
|
|
+ neighbour->g = n_g;
|
|
+ neighbour->d = n_d;
|
|
neighbour->pos = ( jp_entry != NULL ) ? &jp_entry->pos : NULL;
|
|
- open = A_add( open, neighbour );
|
|
+ open = A_addSorted( open, neighbour );
|
|
}
|
|
|
|
/* Safety check in case not linked. */
|