java-topology/defects/naev-0001/patch/naev-0001.patch

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. */