java-topology/defects/simutrans-0003/patch/simutrans-0003.patch
russell@unturf.com 826a18b121 simutrans: 3 CWE-407 defects in halt reconnection, MOAD 0002-0005 CLEAN
simutrans-0001: rebuild_linked_connections() append_unique O(C*H^2) MEDIUM 97x
  - vector_tpl::append_unique linear scan inside double loop over
    goods categories x connections to collect unique connected halts
  - fix: inthashtable_tpl for O(1) membership test

simutrans-0002: add_grund() registered_convoys.is_contained O(C*R) MEDIUM 45x
  - iterates ALL world convoys, each with linear scan of registered
    convoy vector to check membership
  - fix: pre-build hash set of registered convoy IDs for O(1) lookup

simutrans-0003: rebuild_connections() consecutive_halts append_unique O(S^2) MEDIUM 24x
  - append_unique on consecutive halt vectors per category inside
    nested loop over schedules x entries during halt reconnection
  - fix: parallel inthashtable_tpl for O(1) dedup

All three defects are in simhalt.cc halt reconnection paths, triggered
whenever schedules change (line added/removed, schedule edited, station
built). In large games with hundreds of halts and convoys, these
compound during reconnection sweeps.

MOAD-0002: welt (karte_t) is a god object but standard Simutrans architecture
MOAD-0003: CLEAN (no thread_local usage)
MOAD-0004: CLEAN (nettool password printf is by-design tool output)
MOAD-0005: CLEAN (save cache uses hashtable, no unsynchronized pattern)
2026-03-31 13:01:22 -04:00

69 lines
3.2 KiB
Diff

--- a/src/simutrans/simhalt.cc
+++ b/src/simutrans/simhalt.cc
@@ -1222,6 +1222,10 @@
sint32 haltestelle_t::rebuild_connections()
{
// halts which either immediately precede or succeed self halt in serving schedules
static vector_tpl<halthandle_t> consecutive_halts[256];
+ // hash sets for O(1) dedup of consecutive halts (replaces append_unique
+ // linear scan which was O(S^2) per category where S = schedule entries)
+ static inthashtable_tpl<uint16, bool> consecutive_halts_seen[256];
// halts which either immediately precede or succeed self halt in currently processed schedule
static vector_tpl<halthandle_t> consecutive_halts_schedule[256];
+ static inthashtable_tpl<uint16, bool> consecutive_halts_schedule_seen[256];
// remember max number of consecutive halts for one schedule
uint8 max_consecutive_halts_schedule[256];
@@ -1234,6 +1238,8 @@
for( uint8 i=0; i<goods_manager_t::get_max_catg_index(); i++ ){
all_links[i].clear();
consecutive_halts[i].clear();
+ consecutive_halts_seen[i].clear();
}
old_sort_mode = 255; // might result in error in routing
@@ -1296,6 +1302,7 @@
for(uint8 const catg_index : *goods_catg_index) {
if( is_enabled(catg_index) ) {
supported_catg_index.append(catg_index);
previous_halt[catg_index] = self;
consecutive_halts_schedule[catg_index].clear();
+ consecutive_halts_schedule_seen[catg_index].clear();
}
}
@@ -1320,8 +1327,18 @@
if( current_halt == self ) {
// check for consecutive halts which precede self halt
for(uint8 const catg_index : supported_catg_index) {
if( previous_halt[catg_index]!=self ) {
- consecutive_halts[catg_index].append_unique(previous_halt[catg_index]);
- consecutive_halts_schedule[catg_index].append_unique(previous_halt[catg_index]);
+ uint16 halt_id = previous_halt[catg_index].get_id();
+ if( !consecutive_halts_seen[catg_index].get(halt_id) ) {
+ consecutive_halts_seen[catg_index].put(halt_id, true);
+ consecutive_halts[catg_index].append(previous_halt[catg_index]);
+ }
+ if( !consecutive_halts_schedule_seen[catg_index].get(halt_id) ) {
+ consecutive_halts_schedule_seen[catg_index].put(halt_id, true);
+ consecutive_halts_schedule[catg_index].append(previous_halt[catg_index]);
+ }
previous_halt[catg_index] = self;
}
}
@@ -1338,8 +1355,16 @@
if( current_halt->is_enabled(catg_index) ) {
// check for consecutive halts which succeed self halt
if( previous_halt[catg_index] == self ) {
- consecutive_halts[catg_index].append_unique(current_halt);
- consecutive_halts_schedule[catg_index].append_unique(current_halt);
+ uint16 halt_id = current_halt.get_id();
+ if( !consecutive_halts_seen[catg_index].get(halt_id) ) {
+ consecutive_halts_seen[catg_index].put(halt_id, true);
+ consecutive_halts[catg_index].append(current_halt);
+ }
+ if( !consecutive_halts_schedule_seen[catg_index].get(halt_id) ) {
+ consecutive_halts_schedule_seen[catg_index].put(halt_id, true);
+ consecutive_halts_schedule[catg_index].append(current_halt);
+ }
}
previous_halt[catg_index] = current_halt;